Текст
                    Professional

SQL Server 2000

Database Design

Louis Davidson

wrox

Wrox Press Ltd.®

Луис Дэвидсон роектирование баз данных SQL Server 2000 Перевод с английского БИНОМ. Лаборатория знаний 2003
УДК 004.65 ВБК 32.973 ДИ - - . с- - - Переводчик В. А. Ёиаившииков - - Д11 Дэвидсон Л. Проектирование бае данных на SQL Server 2000 / Л. Дввидеан; Пер. с англ. — М.: БИНОМ. Лаборатория знаний, 2003. — 680 е., ил. ISBN 5-94774-074-5 Кайта ааевяшвва вопросам Проектирования йаа данных (БД) с иеаольяоваяи. ем пакета профессионального проектирования SQL Server 2000 фирмы Microsoft. Здесь приведен методически! ивдамд во всем стадиям проектирования ЦД ва. основе опыт» автора, Рассмотрены как вопросы логического проектирования, когда выбирается структура в основные характеристики выы данных, так и во- просы физического проектирования, включая равлизацмю, когда выбираются технические средства, программное обеспечен», пишутся программы, проводит ся необходимая отладка. Автор глубоко не вдавался в «еретические вояроем и использовал только не- обходимые с его точки зрения результаты. Изложенный материал иллюстрирует- ся лишь небольшими примерами, пваваяявицими лучше понять те или иные рас- сматриваемые вопросы. Каира вудат полвана как специалистам до проектированию БД. так и ниже- верам, aftapmamwini ту ней иную ваау меиыа, совдавную на основе SQL Ser- ver 2000, Отдельные части могут выть использованы преподавателями вузов для подготовки курсов по проектированию ваз данных. УДК 004.65 ВВК 3073 По вопросам приобретения обращаться: в Москве «ВИНОМ. Лаборатория внавий» (095)955-03-98, e-mail: lta@aha.ru в Санкт-Петербурге «Диалект* (812)247-93-01, e-mail: dlalec^«ndlct.tefle,rMi.ni ISBN 5-94774-074-5 (русск.) ISBN 1-861004-76-1 (англ.) Copyright © 2001, Wrox Press, Ltd © Перевод » русский язык, БИНОМ, Лаборатория едении, 2003
Подтверждения торговых марок Wrox старался обеспечить информацию о торговых марках всех компаний и продукции, упомянутых в этой книге соответствующим использованием заголовков. Однако Wrox не может гарантировать точность этой информации. Список участников издания Автор Луис Дэвидсон Менеджер серин Брюс Лоусон Дополнительный материал Технические редакторы Гэри Эванс Ховард-Дэвис; Чарлз Хокинс Пол Д жефкот Крис Ульман Гарет Окли Адриан Янг Дуглас Патерсон Технические рецензенты Агент авторе Мартен Вольв Майкл Бенкович Тови Берри Максим Бомвардьер Администраторы проекта Роберт Чанг Аврил Корбин .. . Джемс Р. Де-Карли Робив Дьюсон Силмара Лайон Майк Эриксон Координатор производства Эдвард Д. Феррон Скотт Ханзельман Пив Воясон - Марк Хорнер Иичькгущия Симон Ровинсон Вилл Джонкоке Марк X. Симкин Ричард Уорд Рисунки Хельмут Уотсон Захр Юнес Шабнам Хуссейн Дополнительные рисунки Технические конструкторы Пип Вонсон Катрин Адаксандер Симон Хардвеа Кэт Холл Обложка Корректор Шелли Фразьер Фиона Берриман Менеджер производства Симоя Хардвеа
Сведения об авторе Луне — общепризнанный знаток баз данных. В настоящее время ов трудится в качестве разработчика структуры базы данных для Сетей образования Америка в Нэшвилле, штат Теннесси, не говоря уже о написании этой книга и, возможно, еще одной. Он был проектировищком и разработчиком ба» данных на Microsoft SQL Server в течение девяти лет, начиная с его знаменательной неудачи как администратора лекальной сети для Международной церковной штаб-квартиры в Кливленде, штат Теннесси (да, имеется такая в Кливленда, штат Теннесси). Начиная е объединения SQL Server е Visual Basic 1.0, он занимался этим продуктом 8 течение девяти лет, хотя его навыки в Visual Basic прогрессивно ухудшались с версии 4.0, поскольку он сосредоточился прежде всего на SQL Server. Луис выступал на ряде конференций, таких как CA-World (конференция Всемирной компьютерной ассоциации) в 1999 и 2000 гг., относительно решений с ERwin (пакет для проектирования структур баз данных); и PASS 2000 Europe, где он сделал две презентации: одну — по нормализации и другую — по оитимизации запросов, В семье у него жена Валери и дочь Крисси, с которыми он мечтает провести хотя бы один свободный день с тех пор, как установил на своем столе SQL Serve* и Visual Basic девять лет тому назад... Благодарности Эта книга посвящена памяти моего отца Я хотел бы также поблагодарить следующих лиц, которые непосредственно или косвенно влияли на появление этой книги: Q Господа нашего Иисуса Христа, кто создал меня со способностями проектировать и создавать ко-лпьютерное программное обеспечение. □ Мою жену Валери и дочь Крисси, которые не видели течение шести месяцев без переносного компьютера, даже во время отпуска; благодарю их за любовь и терпение. □ Мою маму, без которой ... О Чака Хокинса за помощь в преодолении моих затруднений по материалам главы 13. □ Чипа Брокера, Доналда Пластера, Майкла Фармера, которые были моими наставниками в течение последних лет. □ Всех влиятельных людей, с которыми я в настоящее время работаю, и которые терпели меня в течение ряда месяцев при условии ограниченного сна. □ Всех профессоров, академиков, авторов и т. д., чьи курсы, книги и Web-страницы я читал в течение ряда лет, и которые прямо или косвенно заронили семена знаний в мою память. □ Штат издательства WB0X, кто (за исключением долгих ночей!) общался со мной очень хорошо и сделал все, что произошло. О Редакторов книги, которые помогли сформировать и объединить совокупность хаотичных идей в книгу, которую вы держите в своих руках.
Содержание Введение: 1 Что охватывает эта книга 2 Кто должен читать эту книгу? 2 Что вам нужно, чтобы использовать эту книгу 2 Используемые соглашения по шрифтам 3 Поддержка клиента 4 Часть 1 — Логическое проектирование 5 Глава 1: Введение в методологию БД 7 Введение 7 История структур БД . & OLTP и организация хранилища данных 9 Четыре модуля современной СУБД 10 OLTP 11 Оперативное хранилище данных (ODS) 12 Хранилище данных 13 Витрины данных 14 Структура 16 Учебный пример 16 Реляционное программирование по сравнению с процедурным программированием 17 Организация циклов 17 Доступ к сети 18 Краткий обзор процесса проектирования БД на основе SQL Server 19 Резюме 20 Глава 2: Сбор информации для проекта БД 21 Команда проектировщиков БД 22 Документация и связь 23 Одобрение клиента 23 Минимальные информационные требования 24 Опытные образцы ВД 24
Интервью с клиентами 25 О чем следует отпрашивать " 27 Кто будет использовать данные? 27 ’ Как данные будут использоваться? 27 Что вы хотите видать в отчетах? 27 Где сейчас находятся ваши данные? 28 Сколько эти данные стоят? 28 Как будут данные в новой БД сочетаться с другими данными? 29 Имеются ли какие-то правила, которые управляют использованием данных? Другие источники для определения превил данных Запрос о цене или sanpoc предложений 2S Контракты или наряды на работу клиента ' 30 Соглашения об уровне обслуживания 30 Не забудьте о ревизиях 30 Старые системы ' 30 Отчеты, формы, электронные таблицы - 31 Схема учебного примера 31 Интервью клиенте 32 Предварительная документация 33 Резюме Зв Глава 3: Фундаментальные концепции БД Введение Реляционная модель База данных Таблица Строки и столбцы Ограничение значений столбца только допустимыми значениями Идентификаторы строк Необязательные значения столбца Число строк ; Дополнительные соображения насчет таблиц SQL-операции Ограничение Соединение Произведение Объединение Пересечение Разность Деление Другие операторы Отношения Бинарные отношения Небинарные отношения Последнее замечание относительно реляционной/SQL терминологии 37 37 38 38 39 41 47 49 S0 51 55 - 55 55 56 57 57 58 58 58 59 59 62 63
Определения существенных понятий 63 Функциональная зависимость 63 Зависимость с несколькими значениями - 64 Резюме 64 с о Глава 4: Сущности, атрибуты, отношения и бизнес-правил а 65 Введение 65 Отказ от выбора окончательной структуры на ранних стадиях .... ; 66 Простой пример 67 Определение сущностей 67 Люди 68 Места 68 Объекты 69 Идеи 69 Документы - 69 Другие сущности 69 Список сущностей 70 Определение атрибутов и доменов 72 Идентификаторы 72 Описательная информация 73 Указатели на расположение 74 Связанная информация 75 Список сущностей, атрибутов и доменов 75 Отношения между таблицами 77 Отношения "один к п" 77 Отношение ’имеет" . - 78 Отношение "является’ 7в Отношения "многие ко многим" 79 Список отношений 79 Определение бизнес-правил 81 Идентификация основных процессов 83 Где мы сейчас? 84 Определение необходимых дополнительных данных 85 Повторное рассмотрение с клиентам вв Повторяй) е ваши действия, пока не получите одобрение заказчикаЦ Учебный пример книги " 87 Интервью клиента : ' 87 Документы 90 Перечень объектов банковского счета . 93 Бизнео-правила и процессы , „ ЭВ Метод водопада 100 Ш
Резюме 101 Глава 5: Моделирование данных 103 Введение 103 Методологии моделирования 104 UML 104 IDEF1X 105 Модели действий 105 Модели данных 108 Сущности 109 Ти1 сущности 109 Обозначение 111 Атрибуты 112 Первичный ключ 112 Вторичные ключи 113 Внешние ключи 114 Домены 115 Обозначение 117 Отношения 118 Один т многим 119 Идентифицирующие отношения 119 Неидентифицирующие отношения 120 Обязательное отношение 121 Необязательное отношение 122 Мощность отношения 123 Функциональные имена 124 Другие типы отношений 125 Рекурсия 125 Отношения классификации 127 Многие ко многим 129 Глагольные конструкции (обозначения! 130 Другие метода изображения отношений 132 Описательная информация 135 Учебный пример 137 Модель действий 137 Модель данных 140 Резюме 147 Глава 6: Методы нормализации 149 Почему нужна нормализация? 150 Уменьшение значений NULL 150 Устранение избыточных данных 150 Устранение ненужного кодирования 151 Максимизация кластерных индексов 151 Уменьшение числа индексов на таблицу 151 Хранение “тонких" таблиц 1S1
Процесс нормализации 152 Первая нормальная форма 152 Все атрибуты должны выть элементарными 153 Экземпляры сущности должны содержать одно и то же число значений 165 Зсе экземпляры записей сущности должны быть различны 155 Программирование аномалий, которых следует избегать в 1НФ 155 Признаки, что существующие данные не удовлетворяют 1НФ 158 Вторая нормальная форма 158 Сущность должна соответствовать 1 НФ 159 Каждый неключввой атрибут дсиисвн описывать вес1»:ключ 159 Проблемы программирования, которых можно избежать спомощью 2НФ 161 Признаки. что ваши сущности не удовлетворяют 2 нФ : 162 Кодирование при наличии этой проблемы 163 Третья нормальная форма 163 Сущность должна выть в 2НФ = . 164 Неключевые атрибуты не могут описывать неключевые атрибуты 184 Проблемы программирования, которые можно избежать с поыощыо ЗНФ 165 Признаки, что еащи сущности не удовлетворяют ЗНФ - =; 167 Нормальная форма Бойса-Кодда 108 Признаки программных аномалий 172 Учебный пример 172 Первая нормальная форма 172 Атрибуты должны быть элементарными 172 Все экземпляры записей сущности должны содержать одно и то же число значений 174 Все экземпляры записей сущности должны быть различными 174 Нормальная форма Бойоа-Колда 174 Итоговые данные 175 Несколько полей с одинаковыми префиксами . 175 Каждый детерминант должен быть ключом - 176 Модель 179 Резюме 179 Глава 7: Расширенные темы нормализации 181 Введение 181 Четвертая нормальная форма 182 Тройные отношения 183 Скрытые атрибуты с неоколькими величинами 18S Предыстория атрибутов 192 Дополнительные нормальные формы 195 Денормализация 198 Учебный пример 197 Резюме 203 Глава 8: Завершение фазы логического проектирования 205 Введение 205
Использование данных 206 Создание отчетов 207 Стратегия получения отчетов - - 208 Прототипы отчетов - - ' 210 Определение использования данных и права доступа 210 Безопасность 211 Иэвесттые ограничения структуры ' 212 Взаимодействие с внешними системами 213 Пример 1 — Проблематичная система планирования ресурса Тфадирвтия 213 Пример 2 — Другой пример 'Как не надо делать" 214 Пример 3 — Системы, страдающие от данормализации 215 Дополнительные проблемы, возникающие при взаимодействии с системами третьих лиц 216 Планы преобразования данных 217 Планирование требуемых объемов 217 План проекта 218 Окончательный обзор документации 21В Дальнейшие требования 220 Учебный пример 220 Использование данных 220 Отчеты 222 Взаимодействие с внешними системами 224 План преобразования данных 225 Требуемые объемы 225 План проектирования 227 Безопасность 228 Окончательный обзор документации 229 Резюме 229 Часть II — Физическое проектирование и реализация 231 Глава 9: Планирование физической структуры 233 Введение 233 Проблемы формирования отчетов 235 Размер данных 235 Сложность - ‘ 237 Требования поиска 237 Конкуренция доступа пользователей 238 Своевременность 238 Частота 239 Влияние характеристик 239 Скорость соединения ' 240 Количество данных 242 Бюджет 243 Число пользователей 243 VI
Возможности SQL Server 245 Тиражирование 245 Связанные серверы 246 Сервисы преобразования данных 246 Контроллер распределенных транзакций 246 Объекты управления данными SQL 247 Реализация СОМ-объектов 248 Почта SQL 248 Полнотекстовый поиск 249 Агент SQL Server 249 Примеры основных топологий 250 "Тонкий" клиент в сравнении с "толстым" клиентом 250 ’Толстый" клиент 250 ’Тонкий’ клиент ’ ' 250 То, что в промежутке 252 Клиент и конфигурация данных 253 Классический клиент-сервер 253 Трсхэвеиная конфигурация 254 Сложное использование Web-серверов 255 Глобальная сеть 256 Учебный пример 255 Резюме 257 Глава 10: Планирование и реализация основной физической структуры 259 Введение 259 Средства генерации БД 260 Схема физического проектирования 2в1 т Преобразование нашего логического проекта > - _ 261 Подтипы 262 другие причины отхода от логическо' го проекта - 26€ Таблицы 266 Обозначение 267 Владелец 26S Ограничения 2S5 Столбцы 270 Обозначение 270 Выбор типов данных 272 Точные числовые данные Приближенные числовые данные 272 Дата и время-дата 279 Двоичные данные 282 Строки символов 283 Вариантные данные 285 Другие типы данных 288 Типы данных, определяемые пользое tareoeM 2Я2 Необязательные данные 296
Вычисляемые столбцы - 258 Служебные столбцы 300 Управление параллелизмом 300 Сопоставление {порядок сортировки) 304 Ключи 307 Индексы ’ 307 Первичные ключи 31Б Вторичные ключи 317 Обозначение 31В Другие индексы 31В Отношения 319 Внешние ключи 319 Обозначение 320 Каскадирование удалений и обновлений 322 Взаимные отношения 6Д 324 Распределение деталей вашей БД между разработчиками 325 Информационная схема и системные хранимые процедуры 325 Новые описательные свойства 330 Учебный пример 335 Очистка модели 335 Подтты 338 Упрощение сложных отношений 338 Таблицы доменов 339 Удаление ненужных сущностей 340 Коррекция ошибок в модели 340 Подготовка модели для реализации ... .342 Служебные столбцы 342 Первичные ключи 342 Типы данных 346 Физическая модель . 353 Резюме 355 Глава 11; Обеспечение целостности данных 357 Введение 357 Примеры таблиц 359 Определяемые пользователем функции 360 Скалярные Функции 361 Встроенные функции формирования таблицы 365 Связывание схемы 366 Функции со многими операторами, формирующие таблицы ЗВ9 Что определяемые пользователем функции не могут делать 369 Ограничения 371 Ограничения, связанные со значениями по умолчанию 372 Функции 373 Ограничения-проверки 375 Обработка ошибок, вызванных ограничениями 383 Триггеры 386 Кодирование триггеров 387 Обработка ошибок 397
Триггеры АПИ? - - 400 Триггеры INSTEAD OF 407 Исполыования триггеров INSTEAD OF 411 Код клиента и хранимые процедуры 417 Изменчивые правила . 419 Учебный пример 423 Ограничения по умолчанию 423 Ограничения-проверки 424 Триггеры 431 Удаление времени из дат сделок 431 Проверка согласован»® города и почтового индекса 432 Распределения сделок не могут превышать размещенную сумму „ 433 Необязательные правила 435 Резюме 43В Глава 12: Расширенный доступ к данным и методы корректировки;437 Введение 437 Рассмотрение запросов 438 Транзакции 43® Блокировка ; 443 Основные рекомендации 451 Основные проблемы кодирования 454 Времсинце таблицы : Е г -. t - - 454 Курсоры 459 Обработка значений NUIX 468 Представления 474 Что нового в представлениях’ 478 Хранимые процедуры 481 Возвращаемые хранимыми процедурами значения 482 Обработка ошибок ц 485 Инкапсуляция ‘ 492 Безопасность. 494 Счетчик транзакций 495 Общие способы работы с хранимыми процедурами 500 Восстановление данных -. 500 Создание строк 506 Корректировка строк 508 Удаление строк 511 Пакеты SQL-кода 513 Пакеты с одним оператором 514 Пакеты с нескольжми операторами 514 Компилируемый SQL-код по сравнению с- трегламентиреданым SQL-кодом : : :~ при создании приложений : : ь. . 517 Нерегламентирсванный SQL-код 617 Компилируемый SQL-кед 520 Советы - б- - 523 -- -". к
Рассмотрение безопасности 523 Безопасность на уровне строк 527 Безопасность на уровне столбцов 529 Рассмотрение взаимодействия между БД 530 » Тот же самый сервер 530 Обозначение при использовании того же самого сервера 530 Безопасность на том же самом сервере 530 Корректировка данных на тем же самом сервере 531 Различные серверы (распределенные запросы) 532 Учебный пример 533 Основные хранимые процедуры 534 Процедура размещения сделки 534 Процедура обновления таблицы bank 535 Процедура удаления получателя платежа 537 Процедура формирования списка счетов ‘ . 538 Процедура заполнения типов доменов сделок - 539 Специальные хранимые процедуры 539 Действие по выполнению баланса счета -540 Действие по получению информации о счете -541 Безопасность в учебном примере 542 Резюме 543 Глава 13: Определение требований к аппаратным средствам 545 Введение 545 Типы БД 546 OLTP 546 OLAP 546 Рост OLTP-таблиц S47 Быстрый рост 547 Медленный рост 547 Отсутствие роста 548 Рост OLAP-таблиц 548 Рост пакетов 548 Рост в связи с ростом компании 549 "Нам нужно больше отчетов!" 549 Не забывайте об индексах 549 Вычисление полного размера таблицы 549 Вычисление размера данных 550 Повторное рассмотрение индексируемых структур в виде В-дерева 5БЗ Вычисление размера индекса 554 Размер загрузки транзакции 557 х
Архивирование данных в случае необходимости 559 Стоимость архивирования 560 Детали архивирования 560 Период архивирования 561 Архивирование таблиц фактов с разделением по датам 561 Архивирование таблиц фактов в соответствии с характеристиками 562 Доступ к архивированным данным 563 Характеристики сервера 563 Подсистемы памяти 563 Подсистемы памяти на сервере Windows NT 564 Подсистемы памяти на сервере Windows 2000 564 Управление работой памяти 565 Искусство управления работой 566 Управление работой SQL и узкие места 567 Настройка памяти: ОС и SQL Server 568 Динамическая настройка памяти в SQL Serw 2000 569 Целевая свободная память 570 Настройка памяти для нескольких экземпляров 571 Настройка памяти процесса в SQL Server 572 Настройка работы сервера 573 Подсистемы ЦП 574 Контроль работы ЦП . , 575 Волоконное управление 576 Дисковые подсистемы 576 Основная дисковая подсистема 577 Кэширование записи и SQL Server J - 577 Подсистема RAID - . : л - :: 578 RAID 0 578 RAID 1 579 RAIDS ~ ~ : L " 580 RAID 10 —где 0 + 1 не равняется 1 ‘ 581 Решения для нескольких контроллеров/каналов ... - 581 Настройка диска и контроль работы я- . = : S82 Соединения пользователя 583 Запирание и блокировке 585 Демонстрация блокировки - ш. =-- к - 585 Контроль блокировок .. .. ... .,. , „ _,.; 586 Учебный пример 588 Резюме к 594 Глава 14: Завершение проектирования 595
Настройка функционирования 596 Поддержка БД только для чтения 598 Моделирование изменений 600 Метод, ориентируемый на таблицы 601 Метод ориентируемый на решение . 605 Реализация 607 Время ожидания 809 Реализация 610 Использование 613 Задачи простого формирования отчетов 614 Оперативное )фанилище данных 615 Модели данных предприятия 618 Переход от опытного варианта 819 Разработка 621 Аппаратные средства разработки 621 Проверка характеристик 621 Аппаратные средства проверки характеристик 622 Реализация 622 Обслуживание и планы восстановления поломок 622 Учебный пример 824 Резюме 828 Приложение А: 12 правил Кодда для СУРБД 629 Правило 1: Информационное правило 629 Правило 2: Правило гарантированного доступа 630 Правило 3: Систематическая обработка значении NULL 630 Правило 4: Динамический оперативный каталог, основанный на реляционной модели 630 Правило 5: Правило общего языка манипулирования данными (ЯМД) 631 Правило 6: Правило корректировки представления 831 Правило 7: Операции добавления, корректировки и удаления высокого уровня 632 Правило 8: Физическая независимость данных 632 Правило 9: Логическая независимость данных 632 Правило 10: Независимость целостности данных 634 Правило 11: Независимость распределения 634 Правило 12: Правило единственности 834 Заключение 635 Предметный указатель 637
Вали вы, находясь в вашем любимом книжном магазине, внимательно листаете эту книгу, потому что это — книга издательства WROX, я знаю, что вы, вероятно, думаете: -Агде же здесь все про коды, настройки и всякое подобное?" Да, это книга не совсем такого рода. (Нет ничего предосудительного в этом виде книг; у меня на моем рабочем стазе их тоже очень много.) Что я хотел соврать воедино в этом случае — это создать книгу по Проектировашно бае данных (ВД), которая тонко балансирует между ориентированными на реализацию книгами по SQL Server, сосредоточенными на настройках DBCC (database consistency checker — модуль контроля непротиворечивости ВД), выборе индексов и использовании всех кнопок и средств управления SQL Server, а одной стороны, и в полном смысле слова академическими томами, которые уходят глубоко в твори» ВД, ио дают немного иля совсем никакой практической информации, с другой стороны. Эта книга охватывает процесс реализации ВД с момента, когда кто-то говорит вам, что он хочет иметь баву данных, всю процедуру формирования таблиц и осуществление доступа к этим таблицам, Она включает также нормализацию «блиц вплоть до пятой нормал ьиой формы, Цель этой аш проста; создать лучшую в мире книгу, такую, что если вы купите десять ее екземпл^ов, пошлете их десяти вашим лучшим друзьям, и попросите их сделать то же самое, я смогу купить прелестный компьютер с DVD-плеером. Реальная цель, однако, не столь очевидна. Я рассмотрел в течение ряда лет большое количество созданных программ, и независимо от того, насколько айрмт’баяиШДйя объекта, сопровождающий код, или даже сопутствующая документация, сама ВД обычно находилась в диапазоне от плохого до ужасного. Мое желание состоит в том, чтобы обеспечить информацией конструкторов ВД, что поможет им создавать надлежащие БД. Для
любого, кто имел удовольствие читать учебники по БД для колледжей, ясно, что они несколько суховаты, — что-то вроде пустыни Мохаве в засуху. Это довольно плохо, так как большая часть пропущенной в них информации полезна и уместна. Так что моя переформулированная цель состоит в том, чтобы устранить разрыв между интеллектуальными гениями и нами, рабочими-программистами, и дать необходимые знания, подходящие для реального мира. Что охватывает эта книга Эта книга состоит из двух частей. Первая охватывает логическое проектирование БД, а вторая рассматривает физическое проектирование и реализацию. Каждая из этих частей начинается с введения, содержащего общий краткий обзор, затем резюме тем, охватываемых каждой из глав. Мы не будем воспроизводить данный материал здесь — обратитесь, пожалуйста, непосредственно к соответствующим страницам. Кто должен читать эту книгу? Не любая глава этой книги обязательно потребуется каждому читателю, и части ее также не предназначены для каждого программиста. Мне жаль, что это так, потому что в каждой главе имеется материал, который обогатит их способность проектировать и реализовывать БД. Однако это неизбежное зло — каждый знакомится с тем, что ему нужно. Системный архитектор БД Если вы уже системный архитектор БД, кто отвечает за еоор требований и проектирование ВД (с причастноетью/ответетаенноетью за реяяутятттп), то, пожалуйста, читайте всю книгу. — возможно, пропустив третью главу при первом чтении. Конструктор БД Если вы занимаетесь только реализацией ВД, большая часть этой книги должна втересовать вас так, чтобы вы поняли причины, нечему '‘безумный" проектировщик структуры данных хочет создать ВД с пятьюдесятью таблицами, когда вы думаете, что требуется только три. Первое чтение могло вы включить главу 5 по моделированию данных, главы в и 7 по нормализации и полностью часть П книги, Эта часть описывает все методы реализации ВД. Программист БД Если вы прежде всего пишете код для SQL Server, то при первом чтении вам будет интересна весьма небольшая часть этой книги. Хорошо в атом случае включить в чтение главу 5 по моделированию данных, главы 6 и 7 по нормалиаадаи, далее главы 9 и 10 по реализации БД, совместно с главами 11 и 12 по обращению к данным. Что вам нужно, чтобы использовать эту книгу В первой половине книги мы будем обсуждать логическое моделирование данных. Нет никаких требований к программному обеспечению для работы с агой частью книги.
Во второй части, снязаниой с физическим проентироиияивм, «имидтаеяявурйЯмаиир ддя работы с примерами — установленная копия пакета SQL Server 2000 и QtMy Analyzer (анализатор запроса), который поставляется вместе с пакетом. Это может быть любое издание SQL -Server (версия Personal Edition — версия, которую можно поставить на нашем персональном компьютере), если вы сможете соединить ее с Query Аайужда. Вам потребуется Ваза данных, которую вы можете создать с пользователем, выступающим в роли собственника ВД, поскольку мы будем создавать все объекты с точки зрения собственника БД. . ' Если вы не имеете копии SQL Server, оценку примера можно выполнить на SQL Server фирмы Microsoft, находящемся на Web-сайте www.mieroeoft.com/aql. Используемые соглашения по шрифтам При пролистывании книга вы сталкиваетесь с различными стилями. Это было сделано, чтобы помочь вам легко идентифицировать различные или* информации к не пропустить каких-либо ключевых моментов. Эти стили следующие: - Важная информация, ключевые моменты и дополнительные объяснения показаны выделением подобие этому абзацу. Убедитесь, что вы обратили внимание на них. когда их встречаете. Общие примечания,-вцпожагателйшя информация и краткие замечания : еыглядлт так, как атшп абзац-. □ Кнопки, наторив вы нажимаете на клавиатуре, подобно Ctrl и Delete, показаны курсивом. Q Если ям видите что-то вроде BacRupDB,.ио будет обозначать имя файла, название объекта или заголовок функции. □ Первый раз, когда вы сталкиваетесь с важным термином, он изображается полужирным шрифтом. □ Слова, которые появляются на экране, типа выбора меню, изображаются шрифтом, подобным тому, который используется на экране, например, меню Я1е. Когда образцы кода изображаются первый раз, они оформляжяея следующим образом: I Private Sub Coraaiand_Click - MsgBox ’’Don’t touch me" End Sub С другой стороны, если вы уже видели код, или ои не Относится нейосрёдетйвнно к сути " вопроса, то ои изображается следующим образом: Private ЗяЬ Саииаш1_С1хск Msg-Box "Don't touch же" End Sub -
Поддержка клиента Мы хотим знать, что вы думаете об этой книге; что вам понравилось, что не понравилось, и что, по вашему мнению, мы можем сделать лучше в следующий раз. Вы можете послать ваши комментарии по электронной почте (по адресу feedback@wrox.com). Пожалуйста, не забудьте упомянуть название книги в вашем сообщении. Wrox имеет группу поддержки, так что если у вас появятся любые вопросы, пожалуйста, пошлите их также по адресу вышеупомянутой электронной почты. Исходный код Полный исходный код для примеров, используемых в этой книге, может быть получен с Web-сайта фирмы по адресу: http://www.wrox.carn. Опечатки Мы приложили все усилия, чтобы удостовериться, что вет никак» атиСок в тексте или коде. Однако человеку присуще ошибаться, и поэтому ьпл ечитввм нужным информировать вас относительно любых ошибок: как их определить и исправить. Списки опечаток для всех ваших книг находятся по адресу www.wrox.com. Вели вы найдете ошибку, которая не выла еще выявлена, пожалуйста, сообщите нам, p2p.wrox.com Для поддержки авторов и подобных им лиц служит список адресатов SQL Server. Наша уникальная система обеспечивает взаимопомощь программистов (торговая марка programmer to programmer) с помощью списка адресатов, форумов и сетевых конференций в дополнение к системе нашей непосредственной электронной почты. Можете быть уверены, что ваш вопрос исследуется не только профессионалом поддержки, но многими Wrox-авторами и другими экспертами промышленности из наших списков адресатов. В p2p.wrox.com вы найдете список, специально нацеленный на разработчиков, использующих.SQL Server, которые поддержат вас не только в то время, когда вы читаете эту книгу, но также и тогда, когда начнете разрабатывать ваши собственные приложения. Зарегистрироваться на такую поддержку следует с помощью четырехступенчатой системы: 1, Выйти на p2p.wrox.com. 2. Щелкнуть на sql_setver как на тип списка адресатов, к которому вы желаете присоединиться, зятем щелкнуть Subscribe (присоединиться), 3. Заполнить ваши данные и снова щелкнуть Subscribe. 4. Дать подтверждение электронной почты. Почему эта система дает лучшую поддержку Вы можете пожелать присоединиться к спискам адресатов или можете получать их как еженедельный обзор. Если вы не имеете времени или средств, чтобы получить список адресатов, то можете поискать наш диалоговый архив. Варахло и бесполезные сообщения удаляются, и адрес вашей электронной почты защищен уникальной системой Lyria. Любые вопросы относительно присоединения или отключения списков или любые другие вопросы относительно списка следует посылать по адресу listsupport@wrox.com.
Часть 1 — Логическое проектирование Логическое проектирование ВД — одна иа наиболее важных задач в любом проектировании ВД, и все же, вероятно, также и наименее понятная. Данная часть книги определенно нацелена изменить это, объединяя небольшую теоршо ВД в некоторыми практическими советами, рассматривая также ряд общих методов, которые часто пропускаются. В этой части мы закладываем основы для физической реализации нашего проекта, которой посвящена вторая половина книги, и при этом охватываем следующий материал: □ Глава 1 Введете в методологию БД — Как следует иа названия, приведено краткое представление различных методологий БД, которые обычно используются для реализации полномасштабных систем ВД (СУБД — систем управления БД), типа OLTP-ВД (On-Line Transaction Proceasing — обработка транзакций в реальном масштабе времени), хранилищ данных, оперативных хранилищ данных и витрин данных. □ Глава 2 Сбор информации для проекта ВД — В этой главе мы даем краткий обзор процесса определения требований, которые нужны пользователям СУБД, рассматривая некоторые неясные моменты, где скрываются важные данные. □ Глава 3 Фундаментальные концепции ВД — Основы понимания кон цепций реляционной теории фундаментальны для процесса проектирования ВД я рассматриваются здесь. Это обеспечит основу для разработки нашего проекта. □ Глава 4 Сущности, атрибуты, отношения н бизнес-прявила — В этой главе мы начнем процесс превращения информации, собранной в главе 2, в логический проект нашей реляционной БД, в частности, продумывая сущности, которые нам потребуются.
Часть 1 — Логическое проектирование □ Глава 5 Моделирование данных — Как только мы выделили объекты, нам нужен способ показать и распределить информацию между программистами и пользователями. Модель данных — наиболее эффективное средство для изображения информации проектируемой БД. □ Глава в Методы нормализации ~ Нормализация — процесс использования информации, которую мы собрали в главе 2 и разработали в главе 4 и главе 5, и превращения ее в хорошо структурированный проект модели данных. В этой главе мы рассматриваем правила нормализации, которым должны следовать при проек-тироваиии хорошо структурированной модели нашей системы. □ Глава 7 Расширенные темы нормализации — Эта глава пользуется предыдущей главой, расширяя основные методы нормализации за пределы приемов, знакомых большинству программистов. Таким образом, мы сможем точно отрегулировать наш логический проект, чтобы избежать, насколько возможно, любых аномалий данных, □ Глава 8 Завершение фазы логического проектирования — Как только мы разработали “совершенную" БД, нам нужно вернуться к первоначальным описаниям, чтобы гарантировать, что данные, которые мы собираемся хранить, будут обеспечивать потребности пользователей. В этот момент многие программисты готовы приступить к кодированию, но важно заввршитьлогичеекую фазу проектирования двойной проверкой нашей модели и ее документации, чтобы попробовать минимизировать уровень изменений, которые потребуются, когда мы перейдем к ее физической реализации.
Введение в методологию БД Введение Проектирование БД можно рассматривать как интересную емееь искусства и науки, Наука проектирования БД хорошо проработана, с рядом зрелых методов проектирования структур БД (типа нормализации), которые использовались в течение долгого времени, Фантичеекм они почти древние в смысле компьютерной терминологин, и при проектировании вашей ВД этим правилам не так уж трудно следовать. Как мы увидим, сравнительно просто перевести такое проектирование на создание таблиц SQL Server. Однако, если вы, конечно, ие разрабатываете очень маленькую СУБД, это не завершение задачи, Искусство создания структуры БД проявляется тогда, когда мы должны обеспечить в наших решениях распределение, кластеризацию, избыточность, поддержку круглосуточного и ежедневного функционирования, использование процедур, крюгеров, ограничений, целостности и т, д. Для физической реализации ВД ие существует специальных методов, и норой мудрена определить, какой метод является лучшим. В атон книге будут представлены хорошие методы, которые, наверное, помогут вам проектировать лучшие ВД. Я стараюсь представить информацию таким образом, чтобы быть достаточно ясным для новичков, но в то же самое время полезным даже для наиболее закаленных профессионалов. Одау вещь нужна твердо понять, прежде чем мы пойдем-далее. Проектирование и структура БД по-разному выглядят для- тех, кто занимается настройкой я администрированием ВД. Например, как системный архитектор, я редко формирую учетные записи пользователей, обеспечиваю резервирование. идн yw? формирую средств* жшфаввявд иди кластеризации и т. д. Это — работа администратора ВД. Когда я работал для маленьких магазинчиков, эти типы задач выли в моей области, во я никогда не чувствовал, что делал эту работу хорошо.
В данной книге мы глубоко рассмотрим вопросы, как проектировать, разрабатывать и реализовывать таблицы ВД на SQL Server и сопутствующие им методы доступа; но мы не будем рассматривать детали физических аппаратных средств, системного программного обеспечения и другие аспекты СУБД. История структур БД Структура ВД значительно изменилась за последние несколько десятилетий. Первоначально не было никакого выбора структуры ВД способами оптимизации для ограниченных аппаратных средств ЭВМ, на которых они размещались. В начале 1970-х гг. В. Ф. Кодд, преподаватель математики, в то же время являясь исследователем фирмы ШМ, представил - новую концепцию, которая была предназначена навсегда взманить путь, которым будут размещаться данные. Его принципы, от которых пошла реляционная модель, опередили свое время. Многим программистам понравились его идеи, но они не могли реализовать их из-за ограничений аппаратных средств ЭВМ. Исследование Кодда, реализуется в его трех правилах нормализации. Эти правила развивались, начиная с первоначальной работы Кодда, и иы объясним все общепринятые правила нормализации позже в главах б и 7. Хотя ж и не вуду вепоередствешю ссылаться на too работы, пвяги каждая частица теории реляционных БД построена на его классической статье "A Relational Model of Data for Large Shared Data Banti' (Реляционная модель данных для больших распределенных байков данных). Если вы могли прочитать эту статью или даже книгу К, Дж.. Дейта “An Introduction to Database Systems* (Введение в СУБД), широко известную как техническая "библия", то, наверное, заметили, что они довольно сухие и академические. Я собираюсь представить важные идеи обычным способом, который является уместным для проектировщика ВД и легким для его понимания. "A Relational Model of Data for Large Shared Data Banks'' может выть найдена no адресу http://www1 .acm.org/classics/nar95/toc.htn'i. Реляционная теория ВД продолжала развиваться, и В. Ф. Коддом я др. формулировались все более и более строгие правила относительно структуры ВД. Эти правила, очень ценны и сегодня широко используются, но реляционная модель практически не использовалась, пока не появилась теория. Мы подробно обсудим модель и ее преимущества позже, а пока только учтем, что нормализация требует уве»чвиия числа таблиц БД, и любые совершенствования теории Кодда приводят к дальнейшему увеличению числе необходимых таблиц. Из-за того, что это предъявляет дополнительные требования к аппаратным средствам ЭВМ, обычным программистам БД было нелегко прочувствовать выгоды от более глубокой кормаяизации. Если добавить к этому факт, что болыи ое число таблиц с волыним числом связанн ых с этим соединений вызывают трудности с аппаратными средствами серверов и программным обеспечением 1980-х и 1990-х гг., то нетрудно понять, почему разработчики ВД не могли должным образом структурировать свои БД как из-за технологических ограничений, так и от недостатка понимания. К счастью, последние достижения в технологии позволяют это пересмотреть. За последние десять лет аппаратные средства ЭВМ стремительно улучшались, улучшено и программное обеспечение серверов БД, используя алгоритмы, которые фактически были осязаемы в течение двадцати лет, ио не использовались.из-за ранее рассмотренных ограничений аппаратных средств. Оптимизация серверов ВД для конкретного исиольамания типа OLTP и OLAP (on-Une analytical processing — аналитическая обработка в реальном масштабе времени), плюс тот факт, что Microsoft полностью переписал SQL Server, чтобы работать
оптимально как с платформой Windows, так и с платформой Intel, в последние-несколько лет вызвали исключительное улучшение характеристик и увеличение надежности. Эти факторы, наряду с усовершенствованием операционных систем — ОС (Operating System — OS) и развитием концепций типа организации хранилищ данных (рассмотренных позже в этой главе), позволили создать серверы ВД, которые могут работать со структурированными данными надлежащим образом, как было определено тридцать лет назад. ВД, построенные сегодня, разработаны с использованием лучших структур, но мы все еще плохо разрабатываем ВД предыдущих лет. Даже с хорошим основным проектом, программирование БД может поставить вод вопрос вазу традиционного программирования типа языков С, C++ или Visual Basic, Это потому, что язык реляционного программирования SQL требует, чтобы программист заново продумал вею систему понятий: множества и отношения вместо операторов IF и циклов. Один оператор SQL при традиционном программировании может расшириться до сотен, если не тысяч, строк процедурного кода, и в некоторых случаях это так и есть. Этого не скажешь о Traneact-SQL (Т-SQL), языке SQL Server; он не поддерживает циклы и всякие другие основные конструкции языка прифаммирования, йо даже в Т-SQL, чем большее количество работы отдается реляционному механизму, тем лучше. Другая особенность SQL, которую стоит упомянуть, состоит в том, что ом является отяоситеяьво летним при изучении основ, и как следствие этого, обычно нс воспринимается так серьезно, как другие "Настоящие" языки программирования. Однако программировать сложные вещи на SQL нелегко, и к нему не следует подходить несерьезно. В этой главе читателю представлены концепции, связанные с основным» понятиями БД, в частности, различия между OLTP-и OLAP-ВД; здесь также рассмотрены различия между SQL и Т-SQL с одной стороны и процедурными языками программирования с другой, потому что в дальнейшем мы будем заниматься только разрайотквйсВД. OLTP и организация хранилища данных Об обработке транзакций в реальном масштабе времени (Online Transaction Processing — OLTP) в ВД большинство людей, вероятно, думает, когда говорят о ЕД, что она имеет дело с текущими ляниыми, необходимыми для бизнес-оиернци& Организация хранилища данных — интересное, «восвтельно новое общее понятие БД, которое позволяет для чрезвычайно сложных отчетов стррвть систему исторических данных, выделенных из транзакций. Позволяя нам хранить ваявшие количества исторических данных, оно дает возможность пользователям исследовать долговременные тенденции в истории вианеса, в то же время избегая любых воздействий на текущую деятельность. Имеется огромное различие между этими двумя понятиями, которые могут быть получены в итоге следующим образом: □ Системы обработки транзакций в реальном масштабе времени (OLTP) работают с данными, которые используются в транзакциях. БД оптимизируется, чтобывыстро реагировать на добавление новых данных или корректировку/удалеииа существующих данных. Это делается так, чтобы реагировать даже тогда, когда количество данных очень большое, и имеется большое количество трянаакций. □ Хранилище данных получает результаты, которые OLTP использует при формировании отчетов. Системы OLTP всегда были вялы ири формировании отчетов, так как они оптимизированы, чтобы иметь дело с постоянной корректировкой размещенных данных. Когда при формировании отчета мы хотим проверить данные
и произвести вычисления большого количества дайны*,- это может серьезно ухудшить реакцию нашей ВД. Кроме того, корректировка данных во время формирования отчее* может привести к различным иоедедеетиям^Храшишща- данныж устраняют sr-о, храня данные в форме, которая является эффективной для сложных запросов, — данные оптимизированы для определенного набора вопросов. Кроме того (зто недавняя история размещения данных), вы используете дли запроса не фактическую, текущую версию данных, а копию, используемую только .тля чтения. Это немного упрощено, яа доюето п«волить вам аффективно представить различия между OLTP и организацией хранилища данных. В следующем разделе мы проанализируем - различные модули, которые составляют каждую из этих технологий. Четыре модуля современной СУБД За прошлые десять лет (ияи около того) выло много обсуждений относительно проблемы, как общие данные должны быть структурированы. Мы рассмотрим один as основных вариантов, который выл согласован со многими экспертами ВД. Подход состоит в том, чтобы разбить вею систему на фуннциональные модули, которые обслуживают различные потребности, вместо использования единой технологии, чтобы реализовать все задачи. Эти различные модули следукшще: О OLTP — OLTP-ВД хранит текущие данные — то есть, данные, с помощью которых ВД должна управлять функционированием; здесь необходимо держать .тишь небольшое количество предыстории. □ Оперативное хранилище данных (Operational Data Store — QBS) — объединенные данные, используемые для ежедневных отчетов. Такие данные часто объединяются из нескольких несоизмеримых источников с некоторой степенью предварительной обработки, чтобы сэкономить время запроса. . . □ Хранилище данных — хранилище большого объема данных для размещения почти всех структур данных и их предыстории. О Витрина данных (Det* Mert) — специализированние хранилище данных, оптимизированное для объединений данных, используемых для определенных ситуаций, и представляющее собой подмножество хранилища данных. Витрины данных обычно обрабатываются, используя технологию, известную как аналитическая обработка в реальном масштабе времени (Online Analytical Processing — OLAP). Рассмотрение их как ммилей может показаться некорректным, но термин "модуль" используется здесь, чтобы указать, что она — отдельные части объединенной СУБД. Каждый модуль играет свою специфическую роль. По той или иной причине не всякая СУБД будет требовать всех модулей, Два основных фактора выбора необходимых модулей — количество данных, которые должны выть размещены, и параллелизм (который определяет, сколько пользователей будет пбрятиятмгя к данным в любое конкретное время). Меныпиё системы могут управлять формированием отчетов без создания дорогостоящих дополнительных модулей. Единственная проблема состоит в том, чтобы определить, что составляет меньшую систему? Глава в будет детально развираться е этими проблемами. Посмотрим теперь несколько более пристально на эти четыре модуля, которые мы ввели выше.
OLTP OLTP-ВД содержит данные, используемые в процессе функционирования повседаевных транзакции. Она описывается временными характеристиками, в которых отражается обработка потока, и служит источником, в котором находятся данные относительно предметной области. БД характеризуется наличием любого числа параллельных пользователей, создающих, обновляющих и удаляющих данные. Все общ w_ дачные должны выть размещены или уже выли размещены (в случае исторических данных) s OLTP-ВД, Структура размещения данных a OLTP строится» используя нормализацию. —' специальный метод структурирования, упомянутый ранее. Нормализация-уменьшает количество избыточных данных, помогая предотвратить неприятности ари корректировке — наподобие того, что произошло бы, если бы вы храиили адрес заказчика в двух местах ВД, а изменили вы его только в одном месте. Нормализация подробно рассмотрена в 'лавах 6 и 7. Главная цель OLTP-ВД — целостность текущих общих данных. Это достигается следующими двумя важными принципами: □ Хранить каждую текущую часть данных в единственном месте,- где ее можно отредактировать так, чтобы любое изменение отражалось всюду, где она используется. О Обеспечение транзакционной поддержки так, чтобы многочисленные изменения ВД происходили совместно. Если одао из изменений в транзакции не удается, ни одно из других изменений не должно произойти. Оставшаяся часть транзакции после этой еитуавии должна реализовать откат. Побочный аффект этого, однако, тот, что получение полезной информации может выть затруднено из-за строгой структуры данных. Поскольку OLTP-БД разработана е учетом характеристик транзакций и целостности, даиные размещаются так, чтобы можно было их эффективно запиедаать, ио не обязательно эффективно читать. Пользователь будет часто запрашивать многие таблицы, чтобы получить нужный набор информации. Эта книга нацелена на структуру и реализацию OLTP-БД. Для меньших (снова тот же термин') систем вы можете использовать только этот тип ВД, Как пример использования ВД типа OLTP, рассмотрим банк, который имеет клиентов с различным количествам денег, размещенных на их счетах. Банк будет обычно иметь ВД, которая содержит фамилии и контактную информацию всех клиентов. Наяк также, вероятно, будет яме» много распределенных БД, чтобы обращаться ко всем различным типам счетов и денежно-кредитным сделкам (транзакциям), которые совершают его клиенты. Кажется, что лучшв=все-ата даиные разместить в той же самой БД, но так как банк имеет огромные количества даииых относительно денежно-кредитных сделок, охватывающих различные географические пункты, ипии и даже различные страны наряду е больше* количеством контактной информацт относительно клиентов, более вероятно, что эта части будут размещены в отдельных ВД. Банк может также иметь ВД (или ту же самую ВД, если она так ооганизована) для будущей контактной информации о клиенте и т. д. Каждая из этих БД будет типа OLTP. Банковские системы-находятся среди наиболее слоящих OLTP-БД в сегодняшней глобальной экономике. Чтобывам выло легко использовать кредитную карточку где-нибудь в мире, вез (по всей вероятности) возможности забрать большее количество денег, чем вы фактически имеете н» счете, требуется массивная распределенная OLTP-СУБД, чтобы это реализовать.
Я должен разъяснить то, что я подразумеваю под термином "транзакция", прежде чем двинусь дальше. OLTP-ВД имеют дело не только с денежно-кредитными или числовыми сделками, Термин "транзакция" означает механизм, с помощью которого вы можете гарантировать ссылочную целостность (другими словами, сохраняя определенные отношения между таблицами при вводе и удалении запит) и атомарность (концепция, означающая, что какая-то информация должна выступать как отдельная единица). Для ВД это означает, что вы имеете известный набор результатов в зависимости от того, удается ли или нет какое-либо действие (или группа действий), Мы рассмотрим в главе 12 транзакции и то, как вам их использовать в коде ВД. Наиболее важная вещь, которую следует понять, состоит в том, что одной из главных характеристик OLTP-ВД является использование механизмов, предохраняющих данные ст разрушения при любых действиях пользователя. Оперативное хранилище данных (ODS) Идея ODS состоит в том, чтобы иметь ВД, где все данные, которые вы используете в вашей деятельности, размещаются ежедневно. В зависимости от ваших требований здесь может быть также размещено ограниченное количество исторических данных. OD-S разрабатывается так, чтобы разобраться и заняться некоторыми из проблем, связанными о концепцией OLTP, и которые могут быть подытожены следующим образом: □ OLTP-ВД обычно имеют елоясиую структуру со многими таблицами. Структуры данных могут быть весьма тяжелы для понимания, и миров ^формации может потребовать творческого использования SQL-языка. Исходя из моего опыта, новичок при написании запросов может загубить систему OLTP, написав неэффективные запросы из-аа недостатка понимания внутренней работы SQL Server. □ Многие OLTP-ВД имеют большое количество детализирующих записей. Ежедневные действия, вероятие, не потребуют доступа к каждой сделке, выполненной в течение дня, но будет нужно, вероятно, иметь возможность получить итоговые дачные. Вели вы вы использовали многократные запросы в системе OLTP, все сделки должны были бы быть заново рассчитаны каждый раз, когда будет сделан запрос. □ Не все данные размещаются в единственном источнике. Типичная организация может иметь данные, необходимые для обеспечения деятельности, размещенные во многих источниках данных, Многие, как нам бы и хотелось видеть, уже изменены, многие из общих данных все еще размещаются на универсальных ЭВМ в нереляционных ВД, написанных на языке КОБОЛ. В ODS данные объединены из всех и есоизмеримых источников в общую структуру и суммированы в соответствии е предъявляемыми требованиями. Они могут обновляться так часто или нечасто, как требуется, Данные характеризуются небольшим количеством, если таковые вообще имеются, допустимых корректировок пользователем, в умеренным количеством поддерживаемой предыстории, чтобы формировать ежедневные запросы и показывать теидениии за короткие периоды. В сценарии банка, ODS вероятно имела бы все сделки в течение прошлого дня и, возможно, за прошлую неделю или месяц, размещенные таким образом, когда простой запрос выдаст любые ответы, которые вам понадобятся. Некоторые объединенные данные, если они часто требуются, также могут быть размещены. Например, можно было бы сохранять список счетов клиентов, где можно просмотреть текущий баланс в течение предыдущего дня. Этот вид данных тогда мог бы выть запрошен клиентом, чтобы посмотреть баланс в течение предыдущего дня, а также любые удаленные сделки. Следовательно, итоги всего счета не нужно получать каждый раз, когда клиент хочет видеть информацию о счете.
Дополнительно могли бы выть повлаяы уведомления, основанные, на данных. полученных в результате прокручивания балансов счетов. Хранение полученных итоговых данных не является требованием для ODS. Необходимым может Выть тоялю формирование набора таблиц, которые яюииотея более простыми для запросов, так, чтобы глльзоватади могли выполнять нерегло,чен,'парованные яаиумы. Отличный пример ODS — ВД, помешенная в перекосные компьютеры коммерческого штата, который находится в пути, или в карманных компыотерах для. людей, которые находятсн/ра ботают зне офиса и не имеют постоянного рабочего стола, Следоватеда>ио, цель ODS может выть вьшолнеиа, обеспечивая"польвомтеяей рабочими данными, в которых они нуждаются для принятия ежедневных решений, и храня их актуальными аа короткий период. Хранилище данных Основное использование хранилища данных (Data Warehouse — DWJ — поддержать принятие решения, храня столько исторической информации о структуре, сколько необходимо, Поддержка решеиия — довольно общий термин, который означает возможность ответить на сложные вопросы относительно того,, как функционирует предметная область. Лучшие решения могут быть сделаны, когда доступно большее количество данных, нужных пользователю, которое можно просмотреть, получить их итог и уточнить. Надлежащая система поддержки решения может формировать '’центр сведений" для организации. Например, если коммерческая группа в вашей компания была способна видеть коммерческие тенденции а течение десятилетнего периода, коррелированные с использованием потока рекламы а любое конкретное время, это будет конечно лучше, чем наличие тех же самых данных аа один год. Или даже аа месяц. Другая цель хранилища данных, как и в ODS, еоет-оит в том, чтобы отделить активную транзакционную обработку от аспектов формирования отчетов системы ВД так, чтобы мы могли девать более интенсивные запросы, которые не будут затрагивать возможность наших дользодатодей создавать данные и иметь к ним доступ в тапв системах OLTP. Более старая копия данных из ОЬТР-ВД размещается * хранилище данных. Частота обновления информации определяется количеством данных, потребностью пользователей, и временем, необходимым для выполнения копирования, Эти данные размещаются в виде, удобном для организации запросов, такая структура принципиально отличаеия от той, которая является удобной для .корректировок. Никакая корректировка лживых, ле должна никогда выполняться в хранилище данных; любые изменения должны быть сделаны в оперативной ВД. Только во время, когда хранилище данных меняет свои данные, оно содержит наиболее современный набор информации, получаемой от оперативной ВД. Следовательно, вы никогда не должнь! использовать эти данные в запросах, которые требуют современного точного ответа. Хранилище данных используется исключительно для ретроспективного анализа данных. Полное храни ли ще дянньтх могло -бы сотеря--ять данные да ин-хплекп лет от всех неоднородных источников в пределах организации, таких как наследстаееистем универсальна ЭВМ, ВД на SQL Server и Oracle, и преобразовывать их в единственную БД, использующую общие структуры. Таким образом, ценные данные от наследуемых БД в организации могут быть объединены со всеми более новыми, хорошо структурированными ВД (так же, как ВД третьей стороны, которые используются для различных.задач) а ©дан общий набор структур, которые могут исполыоваться для получения информации, необходимой для принятия решений. Например, данные о людских ресурсах могли бы Дать-получены от одной сторонней системы, главная бухгалтерская книга — от другой, и маршрутизатор IP-адресов — от третьей. Все эти БД могут дать часть задачи, которая обеспечит ваших пользователей полной картиной, по которой они должны принять решение.
Как будет обсуждено позже, одним ив наиболее важных вопросов, который вы должны рассмотреть, когда разрабатываете полную СУВД, является потенпиальный диапазон запросов, которые могут потребоваться. В то время как на многие запросы можно отвечать непосредственно из OLTP-ВД ("Каков текущий баланс счета клиента XT"), рад из них придется направить в хранилище данных ( "Какова средняя сумма дебета и кредите клиентов по банкоматам течение прошлого года в каждом регионе страны?"). Хранилище данных — идеальное .место дая объединения даниых, которые трудно объединить таким образом, как нужно вашим пользователям. Хранилище данных — огромное достижение — формируется не просто. Его не нужно рассматривать лишь как место быстрого формирования ответов на запросы, Вы будете, вероятно, желать вводить данные из многих несоизмеримых источников, некоторые из них могут изменяться, поскольку проходит время, особенно, когда данные поступают от третьих лиц (новый продавец для кадровой ВД, новая версия даугоа системы и, что я предпочитаю, системы от компаний, которые ваша собственная компания только что приобрела — вот всего лишь несколько примеров), Так как хранилище данных банка, по-видимому, будет огромно, его, вероятно, будет тяжело использовать персоналом, осуществляющим банковские реализации и маркетинг. Им понадобились бы данные, чтобы ответить на вопросы типа “Какие из их программ работают лучше всего, когда ...” и "Какой вид программ выбрать, когда.,.”.-Страховой штат, вероятно, смотрел бы на данные е точки зрения тенденций скоростей работы с заложенным имуществом. Наличие такого обширного набора информации в ВД делает новые технологии, заложенные в SQL Server, очень Нежными для организации хранилищ данных. Витрины данных Витрина данных — характерная часть хранилища данных и обычно предназначена для определенных потребностей подразделения или отдела, или для определенной цели бизнеса в пределах организации. Она строится, используя специальные структуры ВД, известные как схемы "Звезда" или "Снежинка". Схемы "Звезда" — фактически простые ВД, с единственной таблицей фактов (таблица, содержащая информацию, которая может быть подытожена, чтобы получить детали относительно истории действий организации), связанной с набором таблиц измерений, которые классифицируют факты в таблице фактов. Следует заметить, что данные в таблице фактов, прежде всего, числовые. Схемы "Снежииа" — просто расширение схем "Звезда", где таблицы фактов могут также быть и таблицами измерений. . - Другой важный термин, используемый-для витрян данных, который мы должны ввести, — кубы. Куб — еще одни довольно странный термин,- ио он описывает, как OLAP-технология организует таблицы измерений нашей схемы "Звезда” или "Снежинка". Измерения таблицы фактов описываются намерениями куба, в то время как каждый алемент данных в кубе представляет факт, содержащий конкретный уровень для различных измерений куба. Рассмотрим следующие три запроса: □ Сколько всего посетителей выло на нашем Web-сайте в 2000 г.? □ Сколько всего посетителей выло на нашем Web-сайте 2000 г. и с какими разделами они ознакомились? □ Какие категории посетителей были на нашем Web-сайте в 2000 г. и с какими разделами они ознакомились?
Каждый из этих запросов будет использовать различное число измерений, чтобы решить проблему, Наша таблица фактов тогда содержала бы числовые индексы пользователей на нижнем уровне, а таблицы измерений будут использоваться, чтобы группировать слой данных в соответствии с потребностями пользователя. Механизмы организации куба могут использоваться, чтобы предварительно объединить ответы на части иет вваианросы, которые может сформировать пользователь, в группы, чтобы сделать ответ иа запрос пользователя более быстрым. Более глубокое объяснение этих структур данных выходит за границы возможностей этой книги. Подробнее все рассмотрено в "Professional SQL Server 2000 " (Wrox Press, ISBN 1861004486) и "Profmifmal Data Warehousing with SQLServer 7.0 and OLAP Services’ (Wrox Press, ISBN 1861002815). Витрины данных могут использовать SQL Server (хотя и не обязаны), чтобы размещать свои данные, но к данным не обязательно обращаться, используя классические команды SQL (хотя, когда SQL Server используется, чтобы размещать многомерные данные, это можно использовать). Вигрижы данных обычно используют OLAP-серверы, чтобы выполнить большую часть агрегмироаяяия до того, как пользователь запросит данные. Существует SQL-подобный язык доступа к данным в витрине данных (Multidimensional Expressions — многомерные выражения — или MDX), хотя технически он яепосредственно ж ив работает с данными из витрины данных. Однако обычно к данным обращаются, используя средства без использования кодирования. Подходящими средствами являются ВИсговоЛ Bxcel, Microsoft Access, Cognos Impromptu, а также Lotus — обращение по имени, которое используется редко. Данные в витринах данных — только для чтения по определению (конечно, исключая процессы, которые периодически освежают их). Главная цель витрины данных состоит- в том, чтобы показать тенденции, региональные или сгруппированные по какому-то другому принципу данные, которые пользователь может пожелать, выделяя содержание массивного Хранилища данных в небольшие сегменты, к которым проще обращаться и управлять ими. Каждая витрина данных будет структурирована таким образом, чтобы решать довольно специфический ряд задач. Рассмотрим пример электронного продавца книг. Витрина данных, которую его отдел маркетинга без сомнения хотел бы иметь, будет содержать информацию о преданных произведениях. Главная таблица фактов содержала вы числовые коммерческие показатели, типа количества пролянных изданий и их цёй1. Тогда намерения были бы смоделированы для дат продажу каким образом пользователи Ж абонентском пункте получают информацию (или они ищут непосредственно издание, или осуществляют общий поиск автора, или мы побуждаем их сделать покупку, высвечивая информацию на экране); информацию о клиентах (откуда они, какими видами личной информации они обладают) и, наконец, произведений, которые были куплены. Из этой информации мы можем, например,.увидеть, что около четверти продаж были сделаны людьми Топека, или получить любую другую комбинацию измерений; таким овразом строятся неопределенные или, наоборот, очень конкретные запросы, чтобы получить ответ. В случав ваших OLTP-БД, это истребовало бы сотен строи кода SQL (и я могу подтвердить это по болезненным личным воспоминаниям). В банковском примере архитекторы данных (или, естественно, .конечные пользователи); могут создавать определенные витрины данных для различных пользователей хранилища данных, Например, пользователь маркетинга может иметь витрину данных е банковскими клиентам я, определяемыми уровнем доходов, регионом страны, количеством оплат, величиной капитала и текущей информацией. Исходя из мота могут быть сформулированы запросы: показать клиентов для каждого региона, е каждым уровнем дохода, кто выполнил свои платежи своевременно, с каким уровнем доходов. Это могла бы быть очень мощная информшщя при определения новых условий специальных тарифов в различных частях страны.
Структура Полная структура окончательного выбора БД зависит от масштаба проблемы, которую вы пробуете решать, В следующей диаграмме мы берем типичную большую систему и делим ее на части, чтобы видеть поток данных через всю БД. Вы можете заметить, что имеется много витрин данных, рассеянных по всей диаграмме. Витрины данных проще всего строить на основе хранилища данных, так как хранилище данных уже имеет историческую и нфсрмацию, но они могут быть построены- ж любом месте по пути следования информации. Обратите внимание, что мы берем наши исходные данные в наследуемых ВД и QLTP-БД в преобразовываем их для использования ODS. На самом дела, мы делаем примерно ту же самую вещь на каждой ступеньке процессе, от ODS до хранилища данных, и на витринах данных. Вообще-то, как только вы преобразовали данные в ODS-подобный формат, вам не нужно будет делать слишком-много изменений, чтобы получить данные в формате хранилища данных, Фактические сценарии могут изменяться от ситуации к ситуации. Учебный пример На протяжении книги мы будем приводить пример процесса разработки ВД. Ои охватит все аспекты проектирования, от начальных его идей, через интервьюирование клиентов, моделирование БД, создание ее, используя SQL Server, выбор аппаратных средств и организацию доступа к ней. Темой примера будет персональный реестр текущего Лета. В то время как фактическое количество данных, которое будет размещено в окончательных ВД (которые расположены на Web-сайте фирмы Wrox по адресу www.wrox.com), будет намного меньше, чем могло бы обычно требоваться для развитого хранилища данных, это — просто пример с легко управляемым размером.
Выбор текущего реестра дай- достаточно свободы действий, чтобы показать, многие из различных типов отношеяий, структурных провеем, и т. д., с которыми приходится сталкиваться при создании реальной ВД. В проект может быть включен некоторый уровень многопользовательского фуакжоироваиия при наличии интерфейса для многих пользователей -текущего счета,, чтобы войти в деветы и кредиты в одно и то же время. Это позволит нам рассмотреть проектирование и реализацию ODS ВД, использование утилит SQL Server, чтобы выполнить задачи извлечения данных из OLTP-ВД, преайразование данных в различные формы, и загрузку их в витрину данных (обычно называемые какЕТЬ — Extract, Transform, and Load — извлечение, преобразование и загрузка}. Реляционное программирование по сравнению с процедурным программированием Система понятий реляционного программирования настолько отличается от процедурного ирограммзгрования. что может вызвать много огорчений, если эти различил будут даправильно истолкованы. Реляционное программирование — это все, что связано с определением того, что вы хотите, в затем запуск реляционного юхвяшт для вьшодаения всей раСюты, а процедурное програмжровавие — все, что связано с эффективным выполнением циклов. Первичный реляционный язык (и единственный, который ш будем рассматривать с этого времени) — SQL. Предполагается, что вы, читатель, имеете опыт в использовании SQL, щждетльку мы (^дв< представлять все наши примеры в Т-SQL — языке Microsoft SQL. Пока существуют различные мнения относительно глубины различий между этими двумя принципами, различные методологии, которые каждый из них влечет за собой, означают, что нужно быть осторожными при определении того, что наиболее соответствует имеющейся задаче. В главе 3 мы рассмотрим основы реляционной теории более глубоко и выясним, как она связана с SQL. Организация циклов При процедурном программировании основной путь доступа к данным —-оррликывяя- - - циклов с массивами данных» Мы можем представить программу, написанную так, что программист выполнял оператор SELECT, чтобы заполнить массив данных из одной таблицы (назовем ее outerTable), й затем выполняет оператор SELECT, чтобы получить массив данных из другой таблицы (назовем ее innerTable). Тогда он может найти нужную информацию, используя два вложенных цикла следующим образом*. Н Fox Each row In outerTsble В For Each row In innerTable В <... проверить некоторое равенство, ваяем выполнить некоторое действие > В Next row In innerTable t I Next row In outerTable Этот код — то, что SQL Server делает за сценой, когда оя запрашивает дм таблицы. Но это чрезвычайно плохая практика программирования — писать программу подобно этому, поскольку следующий, код SQL исполняет тот же самый запрос. I SELECT ' FROM innerTable JOIN eetertsbl» ’ OR InnerTable.key » outerTable.key
SQL Server оптимизирован я* выполнение этого действия, и сервер имеет более высокую произтевтелышеть, чем клиент. Поив это — простой пример кода, те же самые принципы используются при вы полпенни сложных запросов- всегда лучше передать серверу столько работы, сколько возможно. Это омывается соединением таблиц. Результирующий набор данных, возвращенный запросом, может выть далее отфильтрован и очищен-путем изменения параметров выборки. Важно учиться передавать в SQL Server работу' в максимально возможной степени, так как он непосредственно адедаазяаяен для целей управления данными. Эеемхю&^о важно, потому что некоторые запросы в хорошо структурированной базе данных могут включать десять, двадцать, или даже большее количество таблиц, Чем больше количество используемых таблиц, тем труднее это осуществить с помощью SQL без использования соединений (поскольку соединения представляют отношение afeip^. Bj|gw в 12 жы будем использовать SQL несколько глубже, поскольку рассмотрим, как формировать и организовывать доступ к нашим данным в БД, которые яы создадим. Эта кия, конечно, не руководство по SQL; она в-первую очередь рассматривает, как использовать операторы SQL в проекте вазы данных. Хорошее понимание SQL-языка существенно, чтобы правильно управлять данными в реляционной ВД, и это позволит разработать программное обеспечение для доступа к таким данным» Процедурные прогуаммисты, которые игнорируют это, просто не смогут разрабатывать хорошо работающие приложения. Так же, как вы используете молоток, - чтобы забить гвоздь, и пилу, чтобы распилить древесину, вы должны использовать все инструменты, имеющиеся в вашем распоряжении (включая и процедурные, и реляционные языки), чтобы хорошо выполнить работу. Для дальнейшего чтения и* эту тему используйте следующие книги:. □ Instant SQL Programming (Wrox Press ISBN 1874416508) □ Professional SQL Server 2000 Programming (Wrax Press ISBN 1861004496) □ The Essence of SQL: a Guide to Learning Most of SQL in the Least Amount of Time, (Peer to Peer Communications, ISBN 0964981211) Доступ к сети Одна из первопричин, по которой вы. дазжаы создать надлежащий код SQL, — доступ к сети. Приложения SQL БД, как правило, рас:iределены между двумя ит большим числом компьютеров. В настоящее время довольно редко бывает «эдиуячмитый пользователь SQL БД, который работает на том же самом компьютере, на котором расположены и данные. Хотя э го не всегда так (с SQL Serve1 2000, обладающим Microsoft Dato Engine и версией СЕ, позволяющей создавать системы с единственным пользователем), наша книга будет сконцентрирована на многопользовательских енетемах, которые включают два или больше компьютеров. Однако- все примеры в книге могут выть реализованы и проверены иа любой версии SQL Server. Чтобы реализовать пример организации цикла из предыдущего раздела, каждая строка в innerTable и outerlable должна выть восстановлена через сетевую связь е клиентом. Если таблица большая, это было вы, ероятно, очень медленное действие, даже на относительно быстрой сети. Эффективность, полученная за счет использования ресурсов процессора сервера, позволяет многим пользователям осуществлять доступ к данным более быстро, чем в случае, если работа выполнялась бы на машинах клиента, не говоря уж о том, что количество данных, которые должны быть переданы по сети, будет меньше, минимизируя величину скорости доступа сети. Хотя хорошо разработанные системы централизуют обработку, вы часто видите, что плохо реализованные приложения пытаются делать работу, которую должен делать механизм ВД.
Краткий обзор процесса проектирования БД на основе SQL Server Процесс проектировавия надлежащей СУБД имеет несколько моментов, общих с проектироваввем эффективных компьютерных систем любого назначения. □ Это —- отн оси тел ьн о прямая задача. □ Она может потребовать очень больших временных затрат. □ Она редко получается той мощности, которая требуется. Как мы будем много раз повторять на протяжении всей книги, ВД занимают центральное место среди компьютерных систем, которые создаются сегодня. Даже в системах, которые не предназначены дан ВД, обычно требуется какая-либо форма хранения данных. В этой книге мы концентрируемся прежде всего на больших еиетемах, которые имеют БД в качестве центральной системы. Примеры таких БД — вокруг вас: в Интернете, в наших ваннах, правительстве, компаниях, магазинах бакалеи, аптеках, и т. д. Процесс проектирования таких БД можно развить на несколько шагов, которым мы будем следовать в этой книге: □ Определение цели — не смейтесь над этим, считая все слишком очевидным: большинство проектов портятся разработчиками, не имеющими никакой реальной идеи того, что пользователь фактически хочет Ш в чем нуждается, потому что они перескакивают с одного заключения на другое или не в состояний слушать правильных доводов пользователей, или в некоторых случ аях любых пользователей вообще. В течение этой стадии мы Вудам определять функциональныетребования, требования ж рабочим характеристикам и отчетам для окончательной еистеюя, которую мы будем создавать, □ Логический проект — процесс гтртонтирлняния логического пути достижения целей, которые формируют в пеялияяции независимый путь. Одна из главных причин того, что не придется часто ссылаться на SQL Server в первой полотне этой книги, связаяа с тем, что логический проект ВД независим от физического проекта ВД и ее реализации. □ Физический проект — на этом эта» берется логический проект и приспосабливается к конкретной реализация. Эта стадия проекта связана с определением того, как СУБД будет физически реализована с использованием любых доступных аппаратных средств и программного обеспечения ЭВМ, □ Физическая реализация — стадия реализации проекта связана е фактическим размещением физических данных на серверах ВД и разработкой кода для доступа к данным, ’ □ Обзор — процесс оценки, выли лк достигнуть» цели. Печально, но это наиболее часто пропускаемая стадия проектирования, потому что она требует Фак досадно много времени, и это никакая не забава вообще: тестирование, документация и все другие вещи, которые вызывают в йас крайне неприятные чувства при их выполнении, но мы должны это делать. Это должно включать механизм исподьзования обратной связи от пользователя и рассмотрение плана обслуживания, связанного с исправлением любых идентифицированных проблем, В этой книге первые два шага рассмотрены в части "Логическое проектирование", а остальные — в части "Физическое проектирование и реализация".
Резюме В этой главе мы представили основы современных СУВД, особенно OLTP и методологию организации хранилища данных. Мы установили, что имеются четыре основных части в СУБД предприятие. Они могут быть представлены как: □ OLTP (Система обработки транзакций в реальном масштабе времени) для размещения наших текущих данных ли пи. с ограниченным количеством исторических данных, сохраненных, чтобы принимать самые последние решения. □ ODS (Оперативное хранилище данных) для создания механизма хранения данных, необходимых для принятия ежедневных бизнес-решений, не прерывая пользователей в OLTP-БД. □ DW (Хранилище данных) — не следует путать с общим термином организации хранилища дайнах; DW используется, чтобы хранить -массивные исторические величины, что позволит нам обслуживать объединенную БД, включающую многие корпоративные ВД и видать тенденции за длительные промежутки времени. □ Витрины данных — часто путаются с OLAP или кубами (которые являются технологиями, используемыми в запросах на поиск данных а витрине данных); витрина данных используется, чтобы получить срез хранилища данных или, в его отсутствии, OLTP или ODS данных, и позволяет пользователю рассматривать объединенные данные в гибкой форме. Наша книга будет нацелена на рассмотрение данных, особенно данных в OLTP-ВД. Данные и, следовательно, OLTP-ВД, являются единственной, наиболее важной честью любого проекта, Некоторые могли бы аргументировать другое: что, например, более важен надлежащий проект или лаже логическая природа информации (потому что она моделирует сферу деятельности предприятия). Однако очень немного пользователей интересуется природой данных, или же интерфейсом, который они используют дая доступа к ним, если они могут просматривать и управлять данными, как им нужно. Имеется так много устаревших систем, неуклюжих в работе, плохо продуманных, и все же они продолжают и продолжают использоваться, Процессы изменяются, так же, как и идеи, но запасенные данные будут жить намного дольше, чем любой интерфейс или даже процесс. Одна на главных задач администраторов БД — всюду, где это требуется, преоврааовывать данные из одной системы в другую, Независимо от того, как меняется структура ВД от версии к версии, данные будут все же использоваться снова и снова. Вели вы когда-либо что-нибудь программировали, вы, несомненно, не согласитесь с некоторыми из мнений/идей в этой книге. Я полностью принимаю, что эта квита — не евангелие от святого Луки для БД. Мои идеи и мнения сформировались в течение десяти лет работы и изучения БД в дополнение к точкам зрения многих совершенно разных людей, книг, курсов колледжа, и семинаров. Методология проектирования, представленная в этой книге — сумма этих идей, и я очень благодарен другим людям, которых я могу вспомнить. Я надеюсь, что это доказывает пользу обучения, и что через чтение работ других людей и испытания ваших собственных идей вы сформируете собственную методологию, которая удовлетворит вас и сделает вас успешным проектировщиком ВД.
Сбор информации для проекта БД В этой главе мы вернемся к исходном принципам ж обсудим самые первые шаги, которые нужно предпринять, начгаая проектйромете ВД. Получение предварительной информации для нового проекта ВД — типа решения, что мы собираемся хравгть, —являйся одной из наиболее важна», задач, которую вы должны будете решить, хотя могут быть ограничения во времени и материальных-ресурсах,, которые подразумевают, что этот процесс ве будет выполнен настолько полно, насколько это нужно. Построение вашей ВД без адекватного предварительного анализа хожвт быть уподоблено строительству дома на песке, где после того, как будет построен нулевой цикл, пески сдвинутся, и вы должны будете все начать снова. Такие ситуации часто возникают, когда после того, как ВД создана и развернута, оказывается, что необходимо включить в БД пропущенную особенность, что потребует перепроектирования ее е самого начала. Надлежащий анализ — твердая основа того, что будет создан удачный проект вез шероховатостей. При сборе информации для проекта БД вы должны избежать искушения начать выбирать в~ это время какую-либо структуру. Не определяйте на этой стадаш Таблицы, поля и т, д., даже если вы искушенный проектировщик БД. Наивно пытаться моделировать процесс, выбирая единственный путь, пока вы не проконсультировались со всеми сторонами, вовлеченными в проект, выслушали их идеи и потребности. Слишком часто мы начинаем формировать структуру ВД прежде, чем получим достаточные заяиия о: задаче, и это не помогает яй нашим заказчикам, ни нам самим. Эта глава помещена до рассмотрения моделирования данных, чтобы подчеркнуть данный момент.
В процессе проектироваяия ВД обрабатывается много важной информяпию Эта информация будет полезна для других членов команды проектировщиков теперь и в будущем, чтобы понимать и поддерживать систему. При этом должна быть рассмотрена формальная стратегия ведения документации. Команда проектировщиков БД Чтобы начать процесс сбора информации, должна быть организована команда проектировщиков ВД. Хотя комавда и может состоять только аз единственного человека (что, вероятно, будет в маленьких организациях или проектах), имеются, ио крайней мере, четыре роли, которые должны быть заполнены, потому что каждая предназначена для достижения специфичной, существенной цели: □ Деловой аналигик излагает в деталях требования бизнеса пользователей и обеспечивает команду проектировщиков функциональным описанием, которое они перерабатывают в техническое ониоанже. Он также действует как защитник интересов _долуовя*иФЯк убеждаясь, что окончательное проектное решение деловым потребностям клиенте. Деловой аналитик также убеждается, что все контракты, соглашения, и т, д. выполнены. □ Архитектор данных берет функциональное описание и разрабатывает техническую реализацию описания. (Системный архитектор также помогает в этой задаче.) Архитектор данных проектирует все размещение данных и структуру доступа к ним, а также выбирает надлежащие технологии хранения, основанные на том, что необходимо пользователю, В книге рассматриваются только обязанности этой роли. Обратите внимание, что архитектор данных — не то же самое, что адаинистратор БД (Database administrator — DBA), который связан е реализацией и имеет дело с аппаратными средствами ЭВМ, программным обеспечением ж моментами, обеспечивающими глялкую работу. Архитектор данных занимается вопросом, каким образом СУВД, реляционные или другие, объединить вместе и структурировать. □ Системный архитектор отвечает за проектирование полного интерфейса пользователя и промежуточных объектов бизнеса, которые требуются в проекте, и отвечает за выбор внешнего интерфейса и промежуточных технологий. -Есть определенное различие между этой ролью и архитектором данных — последаий является ответственным исключительно за ВД проекта, в то время как системный архитектор имеет дало ео всем остальным, хотя, вероятаю, могут быта.-я некоторые накладки между этими ролями. □ Менеджер проекта — "босс", кто прежде всего отвечает за то, как каждый другой член команды делает авою работу, и занимается планированием. Каждая из этих ролей будет существовать и в процессе проектирования, и в процессе реализации. Имеются другие важные роли, которые также внесут свой вклад, например, спонсор проекта и .та представитель клиента, кто заинтересован в окончательном проекте и обесценивает финансы для развития, испытаний, документации мт. д. Однако четыре роли, внесенные в список выше, — основная группа, а определять другие или рассматривать большее количество деталей вне возможностей этой книги. Причина определения этих ролей вообще состоит в том, чтобы показать, что если различные люди исполняют каждый свою работу, то архитектор данных может сосредоточиться почти исключительно на том, как размещать данные.
Документация и связь Во время процесса анализа имеется одна хорошая привычка, которую вы должны принять — документировать всю информацию, которую вы получаете. Предположим, что вы можете завтра попас®*, вод автобус. Мягче эту мысль можно зыразить так: "Вели со мной что-то случится, кто-то другой будет должен заняться моей работой". Другой, пример— в нашей промышленности становится все более трудно ориентироваться только на лучших служащих. Если кто-то оставляет проект, потому что конкурент предлагает ему необоснованно огромное жалованье, замена будет связана е крутой кривой обучения, чтобы набрать темп, и единственный способ помочь этому — документировать всю информацию. Таким образом, вы должны документировать, документировать, документировать! Ни в коем случае нельзя все держать в вашей голове. Поскольку вы начинаете собирать замечания относительно потребностей пользователей, полезны следующие руководящие принципы: □ Поддерживать набор документов, включающих йЙфоржацню о проекте системы и спйпификяциж. Важные документы, которые следует иметь, включают: выявленные замечания к проекту, документы, описывающие уетйые запросы по изменениям, и записи по веем спецификациям, таким как функциональная, техническая, испытаний и т. д. □ Помимо формальной документации, важно сообщать членам вашего коллектива проеьлировщиков новейшую, ж полную информацию, Разработайте ж поддерживайте общее хранилшце для всей информации. □ Используйте минуты встреч и храните записи каждого предложения, запроса, или идеи, о которых говорили ваши заказчики. □ Записывайте все, что пользователям не нравится. □ Составьте перечень исходных границ проекта и постоянно помните о них. Это предотврати» о* создания чрезмерно большого проекта иаи ив того, который иуясен. Одной из первых работ команды проектировщиков должно выть определение потребностей (формулировка цели иш характеристики цели),- которые описывают ивражстры^оекта. Они будут учитываться и с ними будет происходить сравнение в течение проектирования и реализации, а также после завершения работы. Если, однако, характеристики проекта и цели четко не заданы на стадии определения потребностей, или ничто не записано, то имеется большой шане, что будут конфликты между вами и вашими клиентами, когда мши идеи их не устраивают. Такая неопределенность или нерешительность могла бы вызвать в процессе проектирования ненужные обсуждения, стычки, или даже судебные процессы. Так удостоверьтесь, что ваши клиенты понимают то, что вы собираетесь сделать для ник, и используйте язык, который будет правильно понят, но достаточно определенный, чтобы описать то, что вы изучите я процессе сбора информации, . Одобрение клиента Поскольку-вы выполняете полный процесс проектирования ВД, клиент, без сомнения, изменит свое мнение об именах полей, определениях полей, бизнес-правилах, интерфейсе пользователя, цветах — почти обо всем, о чем сможет, —и та должны быть готовы к Стому. Независимо от того, что клиент захочет или потребует сделать, вы будете должны пнтвтася это выполнить. Клиент является окончательным контролером проекта, ивы должны выть достаточно гибки при обращении с любыми предложенными изменениями, как незначительными, так и существенными.
После каждой встречи суммируйте ваши замечания на языке, который все участники могут понимать, и посылайте им копии, Держите папку со всеми ответами, которые вы получаете, и пересылайте их менеджеру проекта. В любой момент клиент может сказате-вам "Я никогда не говорил это". Если нет никакого документа, подтверждающего то, что вы говорите, то это может привести к неприятностям. Так что я снова повторю — храните документацию, и если вы должны принять решение, которое не понравится клиенту, то должны будете иметь документ, дублирующий наше решение. Лучший способ избежать конфликта, если клиенты будут изменять свое мнение и ‘характеристики поползут", — удостовериться, что вы получаете одобрение вашего клиента на всех стадиях процесса проектирования. Минимальные информационные требования Независимо от того, являетесь ли вы команда! из одаого человека или винтиком в ударной силе проектировщиков из пятидесяти человек, имеется набор основной информации, которую команда проектировщиков должна собрать в течение ранних стадий, если они собираются продолжить процесс проектирования. Для начала вам потребуется груда записей, распечаток, изображений меряна, CD-ROM, загруженных крупноформатными таблицами, копий ВД, документов в редакторе Wort, документов электронной почты, рукописных материалов и т, д. Никакая структура пока не нужна для этой начальной информации, и было Вы, вероятно, лучше в этот момент вообще не иметь никакой структуры, хотя это, конечно, вопрос вкуса. Некоторые утверждают, что вы ие должны только собирать информацию, чтобы идти дальше, а лучше быть организованным е самого начала, чтобы несло завершения информационного поиска вы могли вы задавать все более и более интеллектуальные вопросы, Причина того, что я не верю в эффективность слишком большого количества структур, заключается в том, что в этом случав я могу придавать вес совранной информации. Реальное упорядочивание должно произойти, когда вы начнете анализировать совранную информацию, что мы обсудим позже. Взглянем на начальные стадии процесса проеитирпняния и источники этой исходной информации. Опытные образцы БД Опытные образцы полезны при разработке крупномасштабных систем, которые оправдывают этот начальный шаг. Их целью должно-выть "доказательство концепции" — возможность команды проектировщиков и пользователей конкретизировать критические элементы проекта, от которых будет зависеть успех или неудача. Иногда в качестве архитектора данных вы будете пытаться использовать опытный образец БД, который бьи торопливо разработан, и "заставить его работать" или, еще хуже, "отполировать ВД". Действительно, вы можете унаследовать неструктурированный, неорганизованный опытный образец, и ваша задача будет состоять в том, чтобы на его основе создать промышленную ВД. Действительно, ужасные слова!
Имейте в виду, что опытные образцы должны рассматриваться лишь как диалоговые картинки, чтобы заставить заказчика подаисать контракт с витой компанией. Снова и снова нанимаются консультанты, чтобы разработать опытный образец, который выглядит настолько хорошим, что кажется, что он готов для развертывания яа предприятии. Многие скажут вам "Это напоминает то, что нужно, так почему его выбрасывать?” Главная причина в том, что при создании опытного образца вы комбинировали, быстро стряпали в слепили код, разрабатывая идеи неорганизованным образом. Наллежашая структура и предусмотрительность при этом проигнорированы. Как только молниеносная разработка опытного образца выполнена, вы имеете пользовательский интерфейс и функциональную ВД, но очень мало мысли, или просто никакой, которая была вложена в его структуру, хотя все и выглядит симпатично. Лучше, как только заказчик распишется, начать ва пустом месте; разрабатывая окончательное приложение, используя структурированные а ттпляерживяамыв стандарты кодирования. Очень важно, чтобы как архитектор данных вы работали настолько твердо, насколько возможно, чтобы использовать код опытного образца только как рабочую модель, как часть документации, которую вы используете, чтобы улучшить ваш советвениыи проект. Это поможет убедиться, что вы не пропустите какую-либо критическую часть информации, которая нужна пользователям, типа названия поля, операции поиска или жиже кнопки (которая вызывает элемент данных), ио не говорит вам ничего относительно структурных проблем. Работайте с этими проблемам!' сами. При проектировании ВД предприятия не может быть никаких коротких путей. Интервью с клиентами В большом бизнесе, корпоратавнам мире маловероятно, что архитектор данных будет встречаться е полмователем, ие говоря уж о взятии у веге интервью. Менеджер проекта, деловой аналитик и системный архитектор могли бы дать всю информацию, которая ему/ей потребуется. Однако бывают времена, когда архитектор данных фактически вовлекается в процесс интервью в зависимости от структуры команды проектировщиков. В моем опыте консультанта я был в роли фактического участника интервью е клиентом (совещания по проекту). При случае, я был должен врать интервью у интервьюера, чтобы выяснить противоречия! Клиента интервьюируют, когда проектирование БД действительно начинается. Однако многие клиенты обычно мыслят визуально; они думают, в частности, в терметах форм, Web-страниц и простых пользовательских интерфейсов. Во многих случаях ваши клиенты не будут иметь абсолютно никакого понимания относительно таге, как система создана. Ваша работа в качестве архитектора данных, свяааиная е интервьюированием клиентов, должна сводиться к сопоставленшо их действительных потребностей с тем, как оет сами их трактуют - это касается должным образом структурированной ВД, скрытой за пользовательским интерфейсом. Изменение всех форм при включении нового текстового окна, метки или чего-нибудь еще является относительно простой задачей, давая пользователю ложное впечатление, что создание приложения БД является легким процессом. Если вы хотите подтверждения, покажите пользователю почти законченный опытный образец приложения без поддержки БД. Клиенты могут быть поражены-, что вы собрали кое-что так быстро, но когда запустят его и будут наблюдать, то споткнутся на этом. Редко кому будет понятно, что за фасадом существует что-то еще, а именно, ВД и объекты бизнеса промежуточного уровня —- то, где вея главная работа и выполняется. Исчерпывающее рассмотрение методов интервью — вне возможностей этой книги, но имеется несколько ключевых моментов, которые должны выть упомянуты. Во-первых, слово “интервью" используется вместо слова "опрос". Первое предполагает обмен идеями един на
один, в то время как второе подразумевает навязывание ваших идей оплачивающему заказчику. Вели вы произведете впечатленве вжстимч, человека и попытаетесь внушить заказчику, что он/рна хочет, вы испортите ваши отношения прямо с самого начала. Вудьте тактичны и не создавайте представление, что вы знаете больше, чем заказчик. Пробуйте установить основную тему для интервью так, чтобы были охвачены важные области, по которым вы имеете открытое мнение. В качестве затравки вам будет полезен один простой вопрос: "Что вы хотите от «ото проекта?" Это позволит вашим интервьюируемым рассказать, что они хотят от проекта. Попросите их объяснить свои идеи относительно того, как они достигли бы итого. Это даст вам ясное, представление.того, что они хотят и почему, предотвращая ваши исходные идеи от изменения, когда, начнете ваяиматься проблемами, которые будете решать для клиента. Будьте готовы елушать, делать записи, задавать вопросы, получать разъяснения — и делайте как можно больше записей. Удостоверьтесь, что вы обращаетесь с каждым человеком, у которого берете интервью относительно проекта, как с индивидуумом. Каждый человек будет, вероятно, иметь свою, отличную от других точку зрения. Не думайте, что первый человек, с которым вы говорите, может сказать за остальных, даже если все они занимаются одаим и тем же, или если этот индивидуум —: менеджер. Разговоры "один на одни" позволяют клиентам высказывать мнение без неуклюжих прерываний во. стороны коллег. Помните тот факт, что самые громкие и самые смелые люди могут и не иметь лучших идей, чем у тихого человека, который сидит сзади и ничего не говорит такого, что может выть ключом ко всему проекту. Удостоверьтесь, что вы получаете общие мнения. Я? - > Полезна техника, когда вы говорите клиенту, что понимаете то, что он сообщил, повторяя во время встречи наиболее важные пушегы. Это также полезно для разъяснения неясных моментов я помогает в процессе формирования заметок. Аудио- или видеозаписи встреч иногда исподазуютея, но его мажет показаться агрессивным и создать дискомфорт клиентам, так что они должны использоваться только в ситуациях, тда его действительно необходимо из-за предыдущих проблем обмена информацией. Никогда не делайте ааииеь беседы без согласия интервьюируемых. Вели можно, возьмите яа встречи человека, который не имеет никаких обязанностей, кроме ведения записей. Кан архитектор данных, вы должны будете вспомнить многое из того, что было сказано в течение этих начальных встреч, и ото жизненно важно в представлении того, для чего вы делаете свою работу, Это объясняет важность документации. Если все записывается и регистрируется, а не только запоминается, клиенты могут регулярно просматривать записи. Это означает, что вы не только можете улучшить отношения е вашими клиентами, но и увеличите ваши возможности в идентификации данных, которые они хотят видеть, а также обеспечит команду проектировщиков информацией, требуемой при проектировании конечного продукта. Эта книга возникла по итогам восьми лет создания ошибок в процессе проектирования ВД, и интервью клиента одна и» наиболее критических частей процесса, о-которым я сталкивался. Это может показаться неподходящей темой для опытных программистов, но даже лучшим из нас следует напомнить, что бряцание оружием, запугивание клиентов, сообщение им, что они хотят, до того, как они сами сообщат мм об атом, и даже попытки управлять ожиданиями пользователя, могут привести к крушению даже хорошо развитой системы. Хорошая техника интервьюирования клиента необходима нам, чтобы получить твердую основу для процесса проектирования. Если вы имеете шаткую основу, конечный продукт будет, вероятно, также шатким.
О чем следует спрашивать Очень важно выть уверенным, что, независимо от того, кто бы ни интервьюировался, были выяснены ответы ня следующие вопросы: Кто будет использовать данные? Ответ на это? вопрос может указатк/иа другой персонал, нежели тот, у которого вы хотели вы взять интервью, и будет, вероятно, иметь значение, когда вы будете определять - безопасность системы. Как данные будут использоваться? Представьте, -что вас просят создать ВД учета партнеров. Вам нужно будет знать: □ Будут ли использоваться имена партнеров лишь для обращений ио телефону, подобно быстрой телефонной книге? □ Будем ли мы делать массовую рассылку электронной почты или отйрайлять по почте сообщения персонально членам списков партнеров? Должны ли для этой цели имена быть подразделены на груп пы? □ Будем ли мы использовать имена, чтобы запрашивать атввт по-тачте, вроде гюжс-ртвовании, просроченных векселей или новых инвесторов? =-.= . □ Насколько яажяо иметь правильные титулы для каждого партнера (например, г-н, а не гр.)? Нужна нам любая фонетическая информация — потеряем ли mi продажу, если пользователь неправильно произносит имя заказчика? Знание, для чего ваш клиент планирует использовать данные в системе, действительно, очень важная часть информации. Вы не только поймете процесс, йо м можете тяыке получить хорошее представление о типах даивиим, которые следует разместить, Что вы хотите видеть в отчетах? Отчеты — часто одна из наиболее забываемых частей процесса проектирования. Многие разработчики-новички оетавляют их разработку на самую последнюю минуту. Однако пользователи, вероятно, больше заинтересованы в отчетах, которые формируют данные, чем в чем-либо еще, что вы будете делать. Отчеты используются как основа до принятия жизненно важных решений, и могут либо поддержать, либо разрушить компанию. Возвращаясь к примеру с партнерами, следует задать вопрос, какое имя клиент хочет видеть в отчетах? . □ Имя, фамилия □ Имя, отчество, фамилия О Фамилия, имя О Прозвище Очень важно "попробовать ва зув’ эти проблемы пораньше, независимо от того, сколь малыми или глупыми они ни показались в этот момент. Мы рассмотрим формирование инфраструктуры отчётов позже в нашем проекте, и этот вид информации может вить необходим. ' ' ' '
Одао замечание-предупреждение: в то время как очень важно получить некоторые, идеи - отноеительно того, какие данные необходимы в отчетах, вы должны быть осторожны, чтобы - избежать любых обсуждений относительно их фактического представления. Это — не задача архитектора данных заниматься чем-либо, кроме требований к данным отчетов. Где сейчас находятся ваши данные? Выло бы замечательно иметь полиоетыо новую ВД, абсолютно не имеющую никаких данных, существовавших ранее. Это бы сделало жизнь такой легкой! К сожалению, такого почти никогда не бывает, кроме, возможно, новых компаний, но даже они будут иметь некоторые данные, которые они хранили, когда начали работать. Каждая организация своеобразна. Некоторые имеют данные в одном единственном месте, в то время как даугае рассеивают их по многим местам. Редко, если вообще когда-либо, данные находятся уже » хорошо структурированных ВД, так чтовы-вы легко могли осуществить доетуи. Вели бы вто имело место, вот бы выла забава! Действительно, почему тогда клиент вообще к вам пришел? Клиенты обычно имеют данные в следующих разных местах: □ Универсальная ЭВМ или наследуемые данные Миллионы строк кода, написанного на КОБОЛЕ, все еще используются многими корпорациями. □ Элеь-троииыс таблицы Электронные таблицы — замечательный инструмент, чтобы рассматривать, вырезать и нарезать данные, но являются неподходящим местом, чтобы обслуживать сложные БД, Большинство пользователей знает, как использовать электронные таблицы в качестве ВД, но, к сожалению, не так хорошо рассмотрено обеспечение целостности их данных. □ Настольные БД типа Access Настольные БД >— мощные инструменты, их летке развертывать и использовать. Однако это часто означает, что такие БД построены и поддерживаются не техническим персоналом и плохо разработаны, потенциально вызывая большое число проблем, когда они должны выть увеличены или изменены. □ Картотека Да, все еще имеется много компаний, которые в настоящее время не имеют никаких компьютеров и поддерживают обширные запасы бумажных документов. Ваш проект мог вы просто заменить картотеку системой, основанной на компьютере, или поставить простую ВД, которая регистрирует физическое местонахождение существующих бумажных документов. Данные, которые вы должны включить в разрабатываемую вами БД на основе SQL Server, будут поступать из этих и других причудливых и замечательных источников, которые вы найдете у клиента. Сколько эти данные стоят? Важно также иметь суждения о стоимости данных. То, что данные доступны, не обязательно подразумевает, что они должны быть включены в новую ВД, Клиент должен быть информирован относительно всех данных, которые являются доступными, и обеспечен оценкой стоимости перевода их в новую БД- Стоимость передачи наследуемых даиных может быть высока. Таким образом, клиенту дают возможность принять решение, что можно сохранить фонды для более важных целей.
Как будут данные а новой БД сочетаться с другими данными? Как только вы хорошо проработаете, где располагать все важные данные клиента, можно начать определять, как данные 8 вашем новом решении иа SQL Server будут взаимодействовать е данными, которые останутся в своем первоначальном формате. Это может включать соядаяие связей с универсальными ЭВМ с помощью сложных шлюзов, связи сервера с другими SQL Server и Oracle или даже соединение с электронными таблицами. Мы не можем делать слишком много предположений относительно агой темы в данный момент в нашем проекте. Только зная основную структуру, вы должны этим заняться» что может быть очень полезно позже. Имеются ли какие-то правила, которые управляют использованием данных? Взяв наш предыдущий пример с партнерами, мы могли вы обнаружить что: □ Каждый партнер должен иметь соответствующий адрес электронной почты, □ Каждый партнер должен: иметь соответствующий почтовый адрес. - □ Клиент проверяет каждый адрес электронной почты, используя почтовую программу, и партнер не будет соответствующим партнером, пока эта проверка не будет успешно выполнена. □ Партнеры должны выть подразделены на типы. Будьте осторожны при использовании каких-либо правил, подобных атим. Согласуйте их с клиентом. Ваш конечный продукт может стать недопустимым из-за тогоЛ<го>ы ввели в данные правило, которое клиент не хочет иметь. Другие источники для определения правил данных Кроме интервью, имеются и другие источники, которые вы можете использовать, чтобы найти правила для данных и другие виды информации, важные для проекта. Часто менеджер проекта получает эти документы. Запрос о цене или запрос предложений Имеются два первичных документа: □ Запрос о цене (The Request for Quote — RFQ) — документ с должным образом сформированной спецификацией, которые организация высылает фирмам, чтовы определить, сколько что-то будет стоить. □ Запрос предложений (The Request for Proposal — RFP) — для м еньшего количества сформированных идей, которые органиаация жвяяот распроетряиить, используя свободы ые консультационные уплути. -
Копии arm документов должны быть добавлены к груде информации, которая вам .. понадобится позже в процессе проектирования. В то время кая эти документы вообще состоят из отрывочной информации относительно проблемы и'желательного решения, вы можете использовать их, чтобы’подтвердить первоначальную причину неовходимоети создания СУБД, и для получения более твердого выбора, какие типы дяннщ должны выть помещены в ней. Контракты или наряды на работу клиента Получение копий контрактов — довольно радикальный подход к свору информации для проекта. Откровенно говоря, в корпоративной структуре вы, вероятно, должны будете вороться с руководством, чтобы заставить его понять, почему в» вообще должны ви деть - контракт. Контракты часто трудно читать из-иа языка, на котором они яалисаны. Однако будьте прилежными в фильтровании информации, и вы раскроете основной набор требований к СУБД — требования, которые должны точно выполнить, или в противном случае вам могут не заплатить за работу. Обратите внимание, что важен не только контракт на создание системы, но должны быть также учтены любые-контракты, которые создаваемая вами система должна обеспечивать, Соглашения об уровне обслуживания Серьезной частью контрактов, которая является очень важной для процеесг проектирования — требуемый уровень обслуживания. Она может определять число формируемых страниц в минуту, число записей в ВД и т. д. Не забудьте о ревизиях Когда вы строите систему, то должны учесть, будет ли система ревизоваться в будущем и кем. Правительство, клиенты ШО-9000, и другие клиенты, которые проверяются организациями по стандартизации, вероятно, будут иметь строгие контрольные требования. У других клиентов также будут финансовые контрольные процессы. Эти контрольные планы иогут содержать ценную иифярмапию, которая может использоваться в процессе проектирования. Старые системы Если вы пишете новую версию работающей в настоящее время СУБД, то доступ к существующей системе может быть и благословением, и проклятием. Очевидно, что большая часть информации, которую вы можете собрать об исходной системе, очень важна. Все экраны, модели данных, модели объектов, документы пользователя и т, д. являются чрезвычайно важными для процесса проектирования. Однако если вы не просто лересматриваете существующую систему, очень важно использовать старую систему ВД только как отправную точку. Очень легко думать в терминах доводки существующего кода, использующего все особенности и данные . существующей системы как основание для обновления системы. Иногда это могло бы быть правильным направлением работы, но, вообще говоря, это не так. В_=ввщ»ишнстве случаев, существующая система, которую вы заменяете, будет иметь много проблем, которые должны выть установлены, но не повторены.
Отчет, формы, электронные таблицы Весьма большой процент компьютерных систем построен для заполнения форм - государственных форм, форм компании, всех иных видов форм. Вы должны обеспечить сбор всех этих данных, которые рассеяны по всей компании, и обязательно найти их все. Фактически ясно, что эти источники будут содержать даивые, которые потребуются для вашего проекта, так что удостоверьтесь, что клиент дает вам все такие данные. Схема учебного примера Главная цель примера в этой главе не в том, чтобы проиллюстрировать часть процесса сбора информации, — маловероятно, что архитектор данных будет делать это в болыдиистве команд проектировщиков — а скорее легко управляемый пример, которому мы можем следовать на протяжении книги. Для удобства вообразим, что мы — команда проектировщиков из одаого человека (за исключением некоторой информации, поступающей от делового аналитика) и мы также полностью игнорируем любой вид пользовательского интерфейса. Вы получаете электронную почту от менеджера по программированию, заявляющего, что отдел бухгалтерского учета нуждается к ВД для управления обслуживанием текущих счетов. После того, как вм стретилиеь со своим менеджером по информационным технологиям, вы обращаетесь к менеджеру бухгалтерского учета Сэму Смиту. Необходимо организовать встречу (интервью) с Самом, чтобы понять, что требуется.
Интервью клиента Вот тип записей, которые бы вы делали при это! встрече:
Просматривая записи, можно заметить, что имеется несколько документов, которые мы должны собрать в отделе бухгалтерского учета, если это доступно: регистр чеков, отчет банка, план ревизии и пустой чек. Предварительная документация Типовой регистр чеков Здесь Number — номер; Date — дата; Description — описание; Category — категория; Amount — сумма; Balance — волане; Account — счет; Running Total текущий итог. Здесь и далее даты будут привадиться в американском формате: сначала идет номер месяца, затем число и, наконец, две последние цифры года. Прим, иерее.
Типовой лицевой счет банка Здесь Check — чек; Deposit — депозит; Direct Withdrawal — прямое изъятие. Illi III III Заметьте, что я вычеркнул все числа в вышеупомянутых отчетах. Вели не очень важно иметь точные числа по документам, удаляйте любую важную информацию, которая могла бы вызвать проблемы, если бы попала не в те руки.
Формат потока данных типового регистра банка Столоеи Тип данных Требуется Дата сделки Date только Г:™: “ |: > 1: 1: > m Номер сделки String(20) 4' Описание Striag(lW) : Количество Money & (String {N) означает строку a N символов, Date — тип-дату, Money — денежный тип.} Обратите внимание, что этот поток данных не из нашего анализа. Скорее всего, это — бумажный документ, объясняющий, что банк будет давать клиеяту, чтобы обеспечить - электронный баланс счетов. Типовой чек Это — то, что мы взяли из примера в данной главе. Мы сделали хорошее начало — возможности нашего проекта чрезвычайно маленькие, а мы собрали так много информации, насколько могли в данный момент, но придет время, когда нам потребуется большее количество информации, В последующих главах мы вернемся к этим записям и документации, когда будем формировать непосредственно саму ВД.
Резюме В этой главе мы сконцентрировали внимание на существенном предварительном шаге проектирования ВД — процессе свора информации. Практический вывод из всего — получите настолько много данных, насколько можете, включая и документы, Посмотрите везде — не оставляйте ни одного уголка, который бы вы не обшарили, чтобы получить столько информации, сколько клиент готов и в состоянии вам сообщить относительно решаемой задачи. Иногда вы будете знать точно, как решить задачу даже после первого сообщения по электронной почте. В другой раз потребуется месяц копания вокруг, чтобы получить достаточно информации, чтобы начать заниматься этой задачей. Проектирование хорошо структурированных систем я ВД — трудная работа, которая занимает относительно долгое время, чтобы выполнить все правильно. Первый шаг — понять задачу прежде, чем ее решать. Всегда имейте в ввду, что требуется большее количество времени, чтобы переделать задачу, чем в случав, когда все делается правильно в первый раз. Мы посмотрим, что означает "правильно” на стадии физического моделирования данных при проектировании ВД. Это будет намного позже.
Фундаментальные концепции БД Введение В этой главе я стремлюсь снять завесу мистики над теорией’ в терминологией, используемой многими академиками ВД. Группа, которой я принадлежу — наиболее активная группа "бойцов-фронтовиков", кто фактически проектирует хорошие пользовательские БД, чтобы заработать на жизнь, и вся эта теория кажется очень удаленной от мира, в котором мы живем и работаем. Это может вызвать у вас вопрос, является ли теория проектирования ВД заслуживающей внимания. Конечно, понимание принципов и теории, на которых построена технология, несомненно, очень важно. Например, предположим, что вы отдаете свой автомобиль механику, который не понимает, как работает яажигание, или летите в самолете с пилотом, который не ивмимает теорию полета? Имея это в вида, почему вы должны ожидать, что заказчики придут к вам для экспертизы БД, если вы не понимаете теорию, которая поддерживает проектирование ВД? Вышеупомянутое означает, что некоторые жз терминов и концепций, используемые- академиками, могли бы запутать и расстроить лучших из нас, частично из-за используемой математики. По этой причине имеются некоторые, известные только посвященным термины, которые, казалось вы, должны для большинства программистов означать одну и ту же вещь, но фактически означают кое-что совершенно разное. Однако важно получить некоторое понимание реляционной теории БД, чтобы можно выло разрабатывать - соответствующие проекты для таких систем.
Итак, чтобы понять, о чем бредит академическое сообщество, мы хотим посмотреть на некоторые простые аспекты реляционной теория, сравнивая их с SQL-концепциями и методами, которые нам уже известны. Поскольку имеется много других квит по данному предмету, мы не собираемся зарываться слишком глубоко на территорию академиков; скорее мы собираемся попробовать представить основу полезных концепций ВД. Эта глава представит реляционные концепции в значительной степени с SQL-ориентированной точки зрения. Мы будем часто касаться реализации вещей (хотя эта часть книги не связана с техническими вопросами), которых нельзя избежать при предоставлении нашего теоретического объяснения в терминах концепций, е которыми вы будете знакомиться. Однако мы не увязнем в вопросах реализации; это будет только краткий обзор. Для большей информированности не стесняйтесь рыться в других ивточншсах, предложенных в главе 1. На протяжении этой главы мы рассмотрим: □ Реляционную модель — исследование терминологии, используемой для наиболее значимых блоков; мы будем их строить более поздних главах. □ Определения дополнительных концепций БД. Реляционная модель Яак упоминалось в главе 1, Е. Ф, Кодд изобрел реляционную модель в начале 1970-х и*. В этом разделе мы рассмотрим ее наиболее важные части с точки зрения Microsoft SQL Server. Как мы увидим, реализация с помощью SQL Server имеет много общего с »той моделью, но ве совсем то же самое. Многие реляционные пуристы съеживаются, когда реализуются все разновидности SQL (не только Microsoft SQL Server). Эта книга — не место для обсуждения их возражений, и юл намереваемся избежать любого противоречил в отношении этого, поскольку рассматриваем физический проект и части .реализации иа SQL Server. База данных Первый термин, который мы должны определить, — база данных (ВД). Просто говоря, база данных — собрание данных, организованных для легкого и быстрого поиска и восстановления. Это может быть карточный каталог в библиотеке, ВД на SQL Server или текстовый файл. Технически, нет никакой соответствующей концепции в реляционной теории, но поскольку мы используем этот термин часто, важно, чтобы мы представляли его. В SQL Server база данных — собрание объектов и данных, которые логически сгруппированы вместе.
Таблица Одно из наиболее частых неправильных представлений относительно реляционной медали — то, что термин реляционный связен с отношениями между таблицами. Фактически ои связан с термином relation (математическое название таблицы), который оказывается (почти) синонимом термину "таблица". Кек мы увидим, реляционная модель использует различные имена не только для того, чтобы большинство программистов знало о таблицах, но также и об элементах, содержащихся в них. Центральный, наиболее важный элемент, с которым мы имеем дело — сама таблица. Таблица — физическое представление некоторогоэбъекта, нив реалного-, или мнимого Когда вы проектируете таблицы. то будете рассматривать людей, места, вещи - (существительные), о которых, вы хотите хранить информацию. Понимание концепции рассмотрения таблиц как существительных, а существительных как таблиц, является первым шагом к проектированию соответствующих таблиц» Позже мы исследаем волее подробно, что мы должны хранить в таблицах и других структурах, рассмотренных в этом же разделе. Затем в следующей главе мы начнем рассмотрение, как идентифицировать таблицы и другие объекты. Термин таблица — существенно ориентируемый на реализацию термин и имеет следующий смысл: Упорядоченное расположение данных. Особенно такое, в котором данные размещены в столбцах и строках в существенно прямоугольной форме. Две общих версии таблиц: электронная таблица Excel и результат простого SQL-запроса в БД Northwind (русская локализация этой БД называется Верей). Последняя таблица показана ниже: 30 А* >г
Даже до компьютерных электронных таблиц люди работали та бумаге с таблицами, содержащими строки и столбцы (хотя они и не назывались электронными таблицами). Мы ваглгаем глубже на строки и столбцы позже в этом разделе. Как мы упомянули ранее, таблица известна так отношение, или, более определенно, именованное отношение. Как мы рассмотрим позже, таблицы и представления — оба рассматриваются как именованные отношения, а результирующие наборы — неименованные отношения. Поникание этого поможет вам лучше писать SQL-запросы. - Следующая таблица — очень характерная таблица, составленная для ресторанов о несколькими щфеделяетыми столбцами. Каждый из терминов таблицы будет определен полностью в этой главе. Это не означает, что таблица и отношение — одно и то же. Определение отношения следующее; Отношении структура, составленная из атрибутов (индивидуальные характеристики, типа имени или адреса, соответствующие столбцам в таблице) и кортежей (наборы значений атрибутов, описывающих индивидуальные характеристики отдельных экземпляров, например, заказчиков, соответствующих строкам таблицы). В пределах отношения экземпляры не могут повторяться; каждый должен быть уникален. Кроме того, экземпляры в пределах отношения считаются неупорядоченными; если два экземпляра поменять местами, отношение не изменяется. Это определение может сначала показаться немного запутанным. Не волнуйтесь; все станет более ясным, когда мы пройдам через всю эту главу.
Отношение = очень строгая математическая концепция, основанная на теории множеств, в то время как таблица — физическая реализация отношения со специальными свойствами. То есть, можно сказать, что отношение может использоваться, чтобы определить все таблицы, но не все отношении могут быть представлены как тяВлицы! Лдмяио разумно предположить, что различия между этими двумя понятиями являются столь тонкими, что могут показаться среднему программисту ВД почти бесемыслеяными. Однако мы будем использовать другой термин для обозначения таблицы — сущность (entity). Термин "сущность” часто используется в логическом моделировании., чтобы обозначить концептуальную версию таблицы. Я лично люблю этот термин, поскольку он действительно подчеркивает, что таблица — фактическое представление чего-то. "Таблица" — очень уж абсолютный термин, который имеет совершенно другое значение вне области информатики. Первый раз, когда я сказал моей жене, что построил таблицу, она подумала, что я ничего не делаю по своей профессии, "А я думала, что ты выл е компьютерами" Термин "сущность" имеет меньшее количество издержек и меньше привязан к реализации. В следующей главе, когда мы натаем идентифицировать таблицы, то сначала идентифицируем их концептуально как сущности, чтобы избежать втискивания в структуру, которую таблицы непременно имеют. Строки я с толбцы Строка (или запись) задает экземпляр какого-то объекта, который физически представляется таблицей. Концепция экземпляра связана е объектяо-ориентированным программированием, но интерпретируется- пр-даутому в реляционном программировании. В объектно-ориентированном программировании (ООП) (и даже в объектно-ориентированных базах данных) класс — общее определение, содержащее атрибуты и методы, которые могут работать с конкретными экземплярами класса, обычно ’.-поминаемыми как объекты. В SQL Server мы имеем дело с таблицами, содержащими столбцы И строки. Определение таблицы во многом подобно классу в ООП, а- конкретный экземпляр таблицы — строка, которая аналогична объекту. Поскольку мы не имеем никаких методов для управления данными, специально введенных в определение таблицы, мы должны иметь определенные SQL-операции, которые можно использовать для взаимодействия е данными в таблицах. Главная причина различий, вообще говоря, — проблема реализации. Поскольку SQL в большей степени создан для организации доступа к данным, очень легко в любой момент видеть многочисленные экземпляры SQL-"объектов", так что многие программисты не замечают параллели между объектом ООП и строкой SQL Server. При моделировании данных и объектов в последующих главах мы подчеркнем некоторые из параллелей между методологиями ООП и реляционным программированием БД. Как мы можем видеть на рисунке ниже, реляционная модель существенно отличается названиями строк и столбцов. Строки —кортежм (tuples >— по-английски/ произносится как "купле"), а столбцы — атрибуты. Термин "кортеж" — один из тех забавных технических терминов, который, кажется, или заставит вас сделать паузу и задуматься, клв заставить нас хихикать (что и случилось со мной). Согласно своей легенде, слово "кортеж’' не имеет никакого другого значения, и вы можете найти его не в любом словаре. Возможно, оио было выдумано в течение формирования реляционной модели Кодда, в частности, как слово вез предазятых понятий!, приложенных к нему. Это устраняет беспорядок, возникающий при использовании терминов типа таблица".
В то время как термин “кортеж", кажется, довольно неочевиден многим программистам, атрибут — название намного лучшее для ^едстаалеввя столбца. Каждый столбец должен содержать характеристику или атрибут экземпляра строки или кортежа, или волее просто сказать — каждый столбецсодержит атрибут, который описывает строку. Ясли думать в такой манере, использование столбца становится более осмысленным. Аналогия со свойствами ООП должна быть разумно очевидна для любого, кто использовал ООП. -Столвец — свойство экземпляра (или строки) таблицы (или отношения/сущности). Одни ва принципов реляционной теории —• то, что атрибуты не имеют никакого свойственного им упорядочения. Вы, вероятно, поняли юлиа, что столбцы в таблице имеют свойственное им упорядочение, поскольку SQL-пользователи хотят иметь постоянный набор столбцов, которые будут возвращены независимо от того, как столбцы были эффективно размещены в физической среде. Главный момент здесь заключается в том, что вы никогда не должны задумываться о физическом упорядочении столбцов. Имеется несколько общих правил сортировки столбцов, которым архитекторы данных обычно следуют при проектировании табшщ, типа того, что первичные ключи размещаются слева в таблице, за которыми следуют более важные столбцы, а менее читаемые столбцы размещаются в правой части, но это — только конкретно для удобства программиста или пользователя. Мы рассмотрим это и дальнейшем, когда фактически начнем моделировать таблицы в главе в. Каждая строка в таблице должна содержать один и тот же набор столбцов. Каждый столбец должен иметь название, которое является уникальным среди названий столбцов в той же конкретной таблице. Это может казаться очевидным, но все же стоит упомянуть. Последний момент, который мы должны овсудйть, —- степень отношения, которая соответствует числу столбцов в таблице. Этот термин не используется так часто, но я видел, что он упоминался в некоторых документах, и его стоит отметить для завершенности.
Атрибуты атрибута Я только что дал такое название этому разделу, потому что- оно засташию меня улыбнуться. Однако важно понять, что каждый столбец в таблице имеет- собственные атрибуты, Наиболее важными характеристиками (помимо названия столбца, которое уже рассмотрело) являются следующие: ’ " ’ □ допустимые зпачеиия столбца; □ является ли столбец частью идентификатора строки; □ обязательны ли значения столбцов. В ряде следующих разделов мы будем говорить об этом более подробно. Ограничение значений столбца только допустимыми значениями Таблицы SQL Sersrer имеют несколько средств для ограничения значений, которые могут быть введены в столбец: □ типы данных (основные^; □ определенные пользователем типы данных; □ ограничения; □ триггеры и хранимые процедуры. Каждое из них служит четко своей цели и должно обычно использоваться в перечисленном порядке. В реляционной терминологии, как показано на рисунке ниже, все они рассматриваются под заголовком домены. Домен определяется как множество допустимых значений дм данного атрибута. Мы кратко рассмотрим эти механизмы в следующих разделах.
Основные типы данных Основной тип даиных — самая простая форма домена в SQL Server. Тип значения определен так же, как характерный диапазон входной величины. Одна дополнительная.деталь, которую следует упомянуть, — то, что каждый тип данных, также поддерживает значение NULL, если Мы не определяем иначе. Например: □ Переменная типа integer требует ввода целой числовой величины и может быть между -23! (-2 147 483 648) и +281 (2 147 483 647). О Тип данных tinylnt также требует ввода целой числовой величины, ио имеет диапазон неотрицательных целых чисел между О и 255. О Тип данных varchar (20) требует данные в виде АШЗН-символов в диапазоне от 0 до 20 символов. Вазовые типы данных — набор "примитивных" типов данных, которые определены в SQL Server. SQL Server имеет следующие вазовые тины данных, которые могут использоваться как типы данных столбцов: binary numeric big int real bit smalldatetime char smallint datetime smallmoney decimal sql variant float sysname image text i.-.t timestamp money tinyint nchar uniqueidentifier ntext varbinary nvarehar varchar Есть еще два типа данных: cursor и table, хотя они никогда не используются в качестве типов данных столбцов. Выбор точного типа данных столбца один из наиболее важных выборов, которые вы будете выполнять на стадии физического проектирования. Во время логической стадии проектирования, как минимум, нужно определить общий тип данных, которые должны быть размещены. Общие классы типов данных могут включать: Binary (двоичный) Blob (BLOB-тип) Character (символ) Date (дата) Logical (логический) Numeric (числовой) Time (время) Мы обсудим типы данных более детально в главе 10.
Определяемые пользователем типы данных fc правилами) SQL Server позволяет создать настраиваемые типы данных, основанные -да существующих типах. Такой тип определяется только я терминах единственного базового -типа данных- Далее можно создать правило и связать его с новым типом данных, которое ограничивает размещаемые значения, допустимые в столбце, по своему желанию. Это позволяет вам создать много столбов из одаого н того же шаблона и обеспечить контроль вводимых значений. Вели вы не знакомы е определяемыми пользователем типами данных, для иолучения дополнительной информации проконсультируйтесь в SQL Server Book# ОаИив (электронное руководство по SQL Server). В качестве простого примера вы могли бы пожелать задать следующие определяемые пользователем типы: □ integerFromOneloTen — определяемый как целое число, е правилом, которое ограничивает вводимые значения в диапазоне от единицы до десяти включительно. □ SoclalSecurltyNwibeK — определяемый как строка из девяти символов илв строка из одиннадцати символов, которая ограничена наличием черточек я четвертой и седьмой позициях, а все другие символы — цифровые. Как вы видите, многие общие типы данных могут выть созданы, чтобы помочь в реализации домена значений, допустимых дай столбца. Нужно также отмстить, что определяемые пользователем тали данных не должны выть простыми основными типами данных, как определено н реляционной теорий. Допустимо иметь более сложные типы данных, пока в них нет повторяющихся групп. Например, вы могли бы иметь следующий тип данных; □ 2DGraph₽oint — определенный как X = целое таело, Y — целое числа Но вы не могли бы иметь тип данных таблицы с неограниченным числом атрибутов. Это допустимо только в случаях типа графической точки, где две величины X и Y фактически представляют единственный атрибут при таком расположении, Обратите внимание, что Microsoft SQL Server в настоящее время не поддерживает эта. Ограничения Ограничение — другой механизм, который может использоваться, чтобы ограничить значения, вводимые в столбец. □ NULL-ограничения — NULL-ограничение определяет, обязательно ли данные должны быть введены в столбец. Значение NULL в реляционной теории указывает, что атрибут имеет неизвестное значение, обычно представляется какможет быть, Важно понимать, что когда сравниваются два значения NULL, они ннкСТда не равны. □ Проверки-ограничения — механизмы, которые позволяют нам определять допустимый диапазон значений для столбца. Например, мы можем создать ограничение на столбец, который хранит месяц года и допускает только значения от 1 до 12, □ Ограничение внешнего ключа — средства, которые используются, чтобы выть уверенным, что значение в одной таблице соответствует ключу другой таблицы. Они используются, чтобы обеспечить целостность данных и тодаерживвть отношения между таблицами. Хотя мы и ее будем их в данной гяаве рассматривать Волее глубоко, важно просто упомянуть их существование в роли домена для столбца.
Триггеры и хранимые процедуры Вы можете использовать триггеры и хранимые процедуры, чтобы реализовать домен столбца, хотя они вообще-то используются для намного более мои^ыж целей. В атом разделе мы рассмотрим только использование каждого из них в качестве домена. Триггеры — механизмы, которые позволяют коду запускаться всякий раз, когда данные в таблице вводятся или изменяются. В то время как они позволяют нам закодировать сложные правила домена, которые не могут быть записаны ограничения, они но могут регулировать значения, вводимые в отдельны» столбец. Последнее имело бы место только в том случав, если бы йравила Выли слитком сложные для простого ограничения, например при требовании автоматической зачистки для ввода новых значений от удаленного супервизора. Мы исследуем триггеры более подробно в последующих главах. Другой механизм в SQL Server, который формирует домен столбца — хранимая процедура, Хранимые процедуры — не лучший способ делать это, но, опять-таки, могут иметься серьезные основания для их использования (например, запрещение прямого доступа к определенным строкам и/или столбцам данных), Основная задача состоит в том, что для этого случая мы должны учесть вер корректировки для столбца, чтобы фильтровать с помощью единственной хранимой процедуры или повторяемого для каждого случая количества кода, когда пользователь должен обновлять данные. Единственный случай, когда мы должны обратиться к использованию хранимых процедур для формирования домена — это когда выполнение требований необязательно или они изменяются. Мы должны также кратко упомянуть, что в то время как хранимые процедуры могут использоваться, чтобы реализовать домены, то же-самое можно сказам» относительно любого кода, который написан, чтобы вставить данные в столбец. В главе 11 мы рассмотрим триггеры и хранимые процедуры как механизм обеспечения целостности данных более детально. Замечание относительно терминологии В этой книге мы обычно будем использовать термин "домен" на протяжении логического проектирования, чтобы указать описание типов данных, таких как номер (number), целое число (integer), строка (string) и т. д. вместе с общим описанием допустимых значений для атрибута. Мы будем стараться использовать тот же самый домен для всех столбцов, которые имеют одни и те же атрибуты. Например, мы могли бы определить следующие домены в типичной ВД; □ Amount (сумма) — денежная величина, о доменом, который всегда требует значения; □ String (строка) — характерное значение строки, которая содержит алфавитно-цифровые величины и которая всегда требует значения; □ Description (описание) — значение строки, которая используется для дополнительного описания экземпляра таблицы и которая всегда требует значения; □ FutureDate (будущая дата) — значение даты, которая при вводе должна быть больше, чем текущая дата, и которая всегда требует значения; □ SocialSecurityNuinber (номер социального страхования) — строка иа одиннадцати символов формата , которая всегда требует значения.
Когда мы идентифицируем новые атрибуты, » будем смотреть, подходит ли новый атрибут какому-либо существующему домену, и если это так, то мы назначаем соответственно этот домен. Если же это не так, то ми соадаем новый домен. Это даст нам1 атрибуты, которые позволяют строить БД более быстро, более легко, и более последовательно. Мы будем использовать термин бизнес-правила, чтобы указать предикат для таблицы или ВД в целом. Предикат — просто правило, которое управляет значениями в БД. Как пример этого, рассмотрим домен SocialSeaarityNwaber. Этот домен пришел е предикатом, размещающим значение в атрибут, который использует его всегда в формате В то время как мне не очекь-то нравится термин "бизнес-правила", это довольно распространенный сегодня теринн. Заметим здесь, что мы продолжим далее определять бизнес-правила на протяжении всей книги, так как имеется миого различных проблем, связанных с этим очень общим термином. Очень часто ии не будем идентифицировать наши бизнев-правила как часть определений таблицы (или сущности); скорее, они будут помещаться в нашу документацию и реализацию. Идентификаторы строк В реляционной теории отношение яе мож« иметь повторяющиеся кортежи. Те, кто имеет опыт работы с SQL Server, конечно, хотели бы перейти к построению индекса, чтобы иметь дело с этой ситуацией. Однако в реляционной теории не имейся также я никакой концепции индекса, так как индекс — физическое устройство, а, строго говоря, реляционная теория не имеет дело с проблемами реализации. В физической таблице нет никакого ограничения, которое говорит, что ие должны быть повторяющиеся строки. Однако, в практическом смысле, никакие две строки не могут быть идентичными, потому что в деталях реализации скрыты атрибуты, которые предотвращают появление этой еитуятт (типа номера строки ташя, точного местоположения в физическом носителе данных. которое вы не можете фактически видеть). Одна ко это всегда плохо, когда Вы можете иметь повторяющиеся строки по ряду причин. □ Невозможно определить, какая строка является какой; это означает, что мы не имеем никакого логического метода изменения или удаления отдельной строки. □ Если больше чем один объект имеет точке те же самые атрибуты, это, вероятно, опишет тот же самый объект так, что если мы пробуем изменить вдау строку, то другая строка тоже должна измениться; это означает, что мы имеем зависимые строки. Чтобы бороться с этим, мы используем концепцию потенциального ключа. Ключ используется, чтобы обеспечить уникальность атрибута или совокупности атрибутов. Сущность может иметь столько потенциальных ключей, сколько потребуется, чтобы обеспечить уникальность ее столбцов. Так же, как в SQL Server, мы имеем концепцию первичных ключей ж вторичных клю чей. Перви ч ны й ключ используется как пер в ичный идентификатор дня таблицы, а вторичные ключи (реализуемые как уникальные ограничения) — другие поля, по которым должна поддерживаться уииквльиость, как видно из следующей таблицы; 3 1868
Выбор потенциальных ключей — очень важный вопрос. Большой процент ошибок БД возникает оттого, что ие определены все вовможяые потенциальные ключи. Котла мы будем получать правила нормализации в главе в, то обсудим специфические оеобеиноети выбора потенциальных ключей. Составной ключ . Ключи, по определению, могуч быть составлены из любого числа столбцов, хотя лучше стараться ограничивать число используемых столбцов настолько, насколько возможно. Старайтесь иметь определенное число столбцов, насколько это возможно, ио если необходимо, может быть столько столбцов, сколько вы пожелаете. Например, вы можете иметь таблицу Book (книга) со столбцами Publisher_Name (название издателя), Publlsher_City (город издания), ISBN_Nufflber (ISBN-номер), и Book_Name (название книги). Для тех, кто не знает, ISBN — уникальный идентификационный номер, назначаемый книге при ее издании. Из этих атрибутов мы решаем, что можем определить три ключа: Q Pub 1 isher_Natne, Book_Name—очевидно, что издатель, вероятно, выпустит больше чем одну книгу. Также можно осторожно предположить, что названия книг не уникальны в множестве всех книг. Однако, вероятно, истииво то, что один и тот же издатель не будет издавать две книги с одним и тем же названием (по крайней мере, мы предположим, что это так). □ lSBN_Nuntoer — мы уже говорилк, что I SBN_Number .уникален. □ Publisher_City, lSBN_Nu~'ber—так как iSWJftjafce г уникален, то комбинация PublishexjCity и ZSBN_Numbeг также уникальна. Первый и третий ключи, которые мы определили, — сложные ключи. О третьем следует сказать несколько больше. Значение этого ключа •— то, что в каждом городе вы можете повторно использовать ISBN^Number; факт, который является, очевидно, не истинным. Это — очень частая проблема со сложными ключами, когда ои не облу^я-иы должным образом.
Хотя вто немного стороне от темы, важно не путать уникальные индексы с ключами. При работе с ивдеквами могут быть причины выбрать ключ ₽ubliBher_City, TSBW_Nu«ber в нашей ВД на SQL Server. Одашко мы не должны идентифицировать его как иёрвичныЙ ключ таблицы. В глав» 10 мы обсудим реализацию ключей, а в главе 14 рассмотрим реализацию индексов для улучшения доступа к данным. Первичный Ключ Первичный ключ — это ключ, который используется, чтобы дать определенный интерфейс доступа к отдельной строке таблицы. Обычно, это — просто указатель (термин, заимствованный из функционального программирования), используемый для доступа к строке, и он, вероятно, не должен изменяться или, по крайней мере, должен изменяться очень нечасто. В следующем разделе будет кратко введено понятие типа ключа, называемого искусственным ключом, который мы будем использовать на протяжении этой книги, и который будет спасать вас от многих неприятностей во время реализации. Мы обеудам точную логику, определяющую этот выбор, когда будем рассматривать физическое проектирование, но пока достаточно рассмотреть только смысл терминов. Искусственные ключи Искуесгвеижый ключ — изобретение, единственной причиной существования которого является возможность идентифицировать строку. Это обычно автоматически формируемый номер (столбец идентификации), или GUID (Globally Unique IDentifier — глобальный уникальный идентификатор), который является очень большим идентификатором, уникальным на всех машинах в мире. Реализация GUID — вне возможностей ягой книги; однако полная документация доступна в www.mfcrosoft.com. Главная причина использования искусственного ключа состоит в том, чтобы обеспечить ключ, который конечный пользователь никогда не должен видеть и никогда не должен с вим взаимодействовать, Он служит просто как указатель, и Ничего больше. Для тех, кто не понимает, что такте указатели, они являются общими конструкциями, пришедшими из языка С или любого языка программирования низкого уровия, используемыми для указания на местоположение в памяти. Искусственные ключи указывают на положение в таблице. Дяя таблицы должны быть также определены и другие ключи, ияаче это некорректная таблица. Концепция искусственного ключа — своего рода беспокойство для тех, кто в перспективе является пуристом. Так как он не описывает запись вообще, то может ли он на самом деле быть атрибутом записи? Искусственные ключи, вероятно, даже не должны выть упомянуты в части, посвященной логическому проектированию, но важно знать off их существовании, так как они вудут, несомненно, все еще Неожиданно возникать в некоторых логических проектах. В главе в мы обсудим все "за" и "против” такого подхода. Вторичные ключи Любой потенциальный ключ, который не выбрав как первичный ключ, упоминается как вторичный ключ. Вторичные ключи очеяь важны даж успешной работы ВД. По той или ивой причине, большинство таблиц имеет больше чем один способ идентификации. Это особенно важно, когда используются искусственные ключи в качестве первичных ключей. Необязательные значения столбца Это — куча проблем, которые я предпочел бы не раскрывать, но доджей. Таблицы могут иметь столбцы, для которых содержание еще неизвестно или ненужно в даввом контексте. К сожалению, в SQL имеется только один механизм, чтобы обозначить, что это факт. Это — значение NOLL. Из перечня различных целей использования KULL очевидно, что это — не оптимальный путь обработки таких ситуаций. Помещение неправильных данных в столбец
(типа даты 31 декабря 9999) даже более сомнительно, тем NULL, по двум причинам. Во-первых, вы можете впоследствии обнаружить, что хотите хранить данные с точно тем же самым значением, которое вы только что ввели. Во-вторых, вы можете увидеть, что ввод «качения, подобного «тому, может оказаться непонятным пользователю. Закон независимости целостности (номер 10 из 12 законов Кодда — см. приложение А) требует, чтобы никакой столбец в первичном ключе не был необязательным столбцом. Сведенный к единственному предложению он означает: NOLL —плохо. Многие теоретики предпочли Вы, чтобы с HOLL было покончено раз и навсегда. Имеется мяого причин для этого, как мы увидим в более поздних главах, но ж зтот момент мы будем придерживаться очень простого взгляда. NULL определен как неизвестное значение так, что никакие два аяачетия NULL не эквивалентны. Аналогично при реализации NULL рассмотрение значения KULL может иметь несколько возможностей; □ Я не знаю значение, □ Я никогда не Иуда получать значение. □ Значение этом столбце не имеет смысла в данной ситуации. Я предпочел вы видеть набор реализованных значений типа NULL, чтобы знать, почему столбец имеет значение NULL. Имеется ряд методов работы с необязательными данными-Дез NULL, но реализация может требовать использования весьма небольшого числа таблиц. Мы обсудим, как работать со значениями NULL, мсколько глубже в главе 12. Умело сгрог Число строк в таблице имеет специальное название в реляционной теории. Как показано на рисунке, приведенном ниже, это называется мощностью (cardinality). Этот термин редко используется, чтобы указать число строк в таблице; однако ан используется, чи>6ы указать числа отрок, которые удовлетворяют некоторому условию. Мм рассмотрим другую форму мощности позже в этой главе, когда будем обсуждать отношения.
Дополнительные соображения насчет таблиц В атом разделе мы рассмотрим некоторые дополнительные соображения, относящиеся к таблицам в реляционной теории. Внешние ключи Когда ключ — не ключ? Когда sto — внешний ключ. Не шутка, но истина. Внешний ключ — фактически столбец {или комбинация столбцов), чьи значения соответствуют первичному ключу или уникальному ключу в той же самой или другой таблице. Существование внешнего ключа в таблице представляет реализацию отношений между таблицами. Мы обсудим отношения более подробно позже в этой главе. Внешний ключ не обязательно должен иметь уникальные значения. Закон независимости целостности (номер 10 ш 12 законов Кодда — ем. приложение А) требует, чтобы дяж впек величин, не являющихся необязательным внешним ключом ЕД, имелось соответствующее значение первичного ключа в связанной таблице. Представления Представления — его таблицы, которые не существуют как базовые (фактические) таблицы; фактически, они — виртуальные. Представление — реализация именованного отношения, как мы обсуждали ранее в разделе Таблица этой главы. Представления появляются как таблицы для пользователя и должны осторожно рассматриваться в этом качестве. Основанные на законах Кодда (см. приложение А), представления, как предполагается, являются корректируемыми, что сильно увеличивает их способности выполнять свои задачи. Фактически, в SQL Server 2000 мы можем реализовать любое представление как корректируемое, используя несколько навык особенностей. Это будут рассмотрено в деталях части Физическое проектирование книги, когда мы будем выяснять, какие проблемы можно, а какие нельзя решить, используя представления. Представления имеют ряд полезных применений и должны рассматриваться как часть логического проекта, поскольку они могут использоваться, чтобы решать некоторые очевь важные проблемы. Они могут использоваться для следующихцелей: □ Обеспечение безопасности. Включением или исключением определенных столбцов из определения представления, конкретной отменой прав доступа к базовым таблицам ж выдачей прав на представление можно предусмотреть безопасность исключенных столбцов. □ Возможность рассматривать различными пользователями одни й те- же данные различными способами. Строя различные представления данных, пользователи могут долутать представление, которое удовлетворяет их потребностям. Таким образом, данные, которые являются несущественными для пользователя, могут быть скрыты от него, чтобы улучшить читаемость. В дополнение к этому, могут быть установлены отношения с помощью внешних ключей дан отображения пользователю данных из связанных таблиц. Q Обеспечение логической независимости данных. Один из основных принципов реляционной теории заключается в том, что реализация ВД не должна давать никаких различий для пользователей. Йели что-то изменяется в реализации ВД, иожьзовие» не должен это заметить. Представления
могут использоваться почта таким же образом, чтобы изолировать пользователя от изменений базовой таблицы. Очевидно, что представление зависит от структуры основной таблицы, ао ее» вы добавите столбец в основную таблицу, представление не должно добавлять его, если в этом нет необходимости. Если вы удаляете столбец или перемещаете его в другую таблицу, представление можно так изменить, чтобы вернуть отсутствующее значение или получить значение из новой таблицы. Представления — очень важная часть любой реализации реляционной БД. Однако они рассматриваются среда физически» проблем реализации, тая что следующий рва мы обсудим представления имение в этой части книга. Отношения Законы Кодда (см* приложение А) заявляют, что каждая БД должна быть описана на логическом уровне таким же образом, как и данные пользователя, чтобы пояьяователи с надлежащей безопасностью могли использовать один и тот же реляционный язык для рассмотрения пользовательских данных. Реляционная теория рассматривает структуру отношения таким образом, чтобы обеспечить два момента: □ Заголовок — набор пар —• имя столбца и имя типа Ванных, — которые определяют столбцы таблицы. Эво служит прослойкой между определениями, которые ввдит SQL-программист, и фактической реализацией, которую программист СУРБД (системы управления реляционной базы данных, Relational DataBase Management System — RDBMS) обычно реализует в структуре сервера ВД. □ Тело — строки, которые составляют таблицу. В SQL Server и бальшинст&е БД мы чаще рассматриваем каталог или, в терминах SQL Server, системные таблицы как групповое описание таблиц и других структур в БД. SQL Sewer представляет заголовок или каталог набора таблиц, называемый информационной схемой (Information Schema). Мы рассмотрим информационную схему в главе 10. Ограничение значений, которые помещаются в таблицы До сих пор мы имели дело со вначветями ограничений, накладываемых на столбцы, и которые технически накладываются на них. Однако поскольку мы уже рассмотрели концепцию домена столбца, нам теперь просто нужно кратко перечислить ситуации, когда нужно ограничить данные в зависимости от других данных/Мы можем разделить их на три типичных сценария: О зависимости между столбцами; □ зависимости между строками; □ зависимости между таблицами. Каждый из них налагает на реализацию весьма специфические проблемы: О Зависимости между столбцами. Зависимости между столбцами имеют дело с ситуацией, когда значение, размещенное в одном столбце, требует некоторого домена значений в другом столбце.
Например, пусть имеются два поля: haaDriversLicence (наличие водительских прав) и driversbicenseNuBber (номер водительских прав). Вели значение has Driver-License равно False, то не нужно вводить значение Dr Iver sLicenseNunibe г, Обычно вы решаете ату задачу для таблицы е помощью простого ограниченияСНЕ*К (проверка). □ Зависимости между строками, Зависимости между строками описывают ситуацию, где каждая строка той же самой таблицы диктует значения в других строках, Примером этого могла бы быть банковская система. Когда пользователь собирается произвести изъятие со своего счета, предварительно должны быть выполнены все бизнес-операпии, чтобы увидать, имеется ла достаточное количество денег, чтобы сделать изъятие. Фактически это могло вы выть выполнено я иначе, но логически оно должно выглядеть именно так. В общем случае эта ситуация должна быть решена, используя триггер, или, что менее желательно, хранимую процедуру. Одной из реальных проблем, связанных с зависимостями между строками в SQL Server, является то, что достаточно легко предотвратить чрезмерные величины (слишком много записей со слишком большими значениями), но очень трудно предотвратить слишком малые вводимые значения. Мы вернемся к этой теме в главе 12, когда начнем реализовывать этот вид проблем. □ Зависимость между таблицами. Другая ситуация могла вы состоять в том, что если ж одной таблице находится какое-то конкретное значение, то вы не хотите помещать соответствующие данные в связанную таблицу. Примером такого ограничения могла бы быть персональная информация о пользователе. Одна таблица будет содержать запись имени человека и его возраста, а другая таблица может быть предназначена для хранения его адреса й телефонного номера. Вели человек находится в определенном возрасте, может быть фактически незаконным хранить его адрес и телефонный номер. Как и с зависимостью между строками, это также должно быть решено, используя триггер или, что менее желательно, хранимую процедуру. В зависимостях между таблицами существуют те же самые трудности, что и с Зависимостями между строками. Предикаты Мы обсудили предикаты для атрибутов, но теперь нам нужно рассмотреть предикаты, „ которые управляют использованием таблицы. Напомним, что термин предикат, — причудливый путь задания правила, которое управляет значениями в Щ, Предикат для каждой таблицы должен быть сформирован во время логического проектирования настолько полно, насколько возможно. Говоря прозаически, составление, списка предикатоа да я. таблицы нужно не только ра з р абот чи к у, но и пользователю для утверждения этих правил. Предикат должен содержать любое правило, управляющее вводом данных. Довольно трудно атому следовать, но овчинка стоит выделки. В следующей главе мы рассмотрим разработку предикатов для таблиц, с которыми будем иметь дело,
SQL-операции Важный момент, который нужно отметить при овсуждеяии реляционной технологии — реляционные операторы в Т-SQL. Мы ие собираемся слишком углубляться в и© обсуждение, но некоторое упоминание об этих операторах должно быть включено в любое обсуждение реляционной модели просто для законченности. В этом разделе мы кратко рассмотрим операторы отнещииия, составляющие реляционную алгебру, которая является основой для всех SQL-реализаций. Как мы намекнули ранее, зсе наборы результатов, которые возвращаются запросом SELECT (запросом яа выборку) языка SQL, можно рассматривать как таблицы (хотя они физически и ве размещены соответствующим образом). Обратите вжимание, что таблица должна содержать уникальные названия для каждого возвращаемого столбца, но это не всегда выполняется для набора результатов SQL (даже при условии, что должно быть именно им). Важно помнить этот основной принцип, поскольку вы пишете ваши собственные операторыSELECT, и это причина того, что использование SELECT * является осойеино неприятным, что будет подтверждено в глаже 12. Таблицы, которые сформированы в результате любого SQL-действия, считаются неименованными таблицами в противоположность именованным таблицам и представлениям. Первоначально Кодд сформулировал восемь реляционных операторов. Большинство их имеет форму одного отношения в виде оператора с одним или двумя отношениями. Только один из них не соответствовал этой форме н не реализуется в SQL. Следующие реляционные операторы — список операторов, которые первоначально Ямли описаны Коддом в 1972 г, в Relational Completeness of Data Base Sublanguages (Реляционная реализация диалектов базы данных). Они приведены в списке в том порядке, в котором првдетавяеиы и тексте, поскольку каждый логически добавляется к предыдущему. □ Ограничение (Restrict) □ Лавер (Project) О Соединение (Join) □ Произведение (Product) □ Объединение (Union) □ Пересечение (Intersect) □ Разность (Difference) □ Деление (Divide) Есть весьма много исследований в этой области, и совсем немного операторов было добавлено за эти годы. Исходные восеть используются, чтобы дать краткое представление реляционных операторов. Мы кратко рассмотрим каждый из исходных операторов и оставляем читателю право выбора их чтения по своему усмотрению. Важно не путать аги операторы с любыми клтег-м словами, используемыми в конкретном SQL-диалекте. Конкретный набор SQL-операторов является результатом двадцатилетних попыток создать реляционный язык, который конечные пользователи могли бы легко использовать, в то же время реализуя громадное число операций.
Ограничение Оператор ограничения используется, чтобы ограничить строки таблицы в соответствии с данным критерием. Это прямо аналогично простому выражению WHSRE запроса SELECT. Например: SELECT fieldname, f-elacan-eZ, ... , fieldnameN FROM tablename £ WHERE fieldname > value Выражение WHERE — реализация оператора ограничения, и можно рассматривать результат этой операции как неименованную таблицу с единственным полем fieldname и содержащую все строки, где fieldname больше, чем заданное значение value. В выражении WHERE мы можем использовать любой из обычных операторов сравнения >, с, >>, LIKE и т. д.). Набор Оператор набора используется, чтобы ограничить число столбцов в результирующей выходной таблице. В следующей примере мы предполагаем, что имеется более одного столбца в таблице: Ц SELECT DISTINCT fieldname, fieldname2 FROM tablename Таким образом вы можете последовательно подразделить результирующую таблицу на отдельные столбцы иш комбинации столбцов. В реляционной теории любые повторяющиеся значения могут быть удалены, если мы добавим ключевое слово DISTINCT языка SQL. Обычно возникает вопрос: "Почему в реляционной теории нет никакого оператора DISTINCT?". Каждый оператор в нашем обсуждении здесь состоит из одного или двух отношений и возвращает отношение. По определению, отношение ие имеет никаких двойных строк. Так как результатом нашего запроса должно быть отношение, он не будет иметь никаких дубликатов, и оператор DISTINCT принципиально избыточен. В сущности, поэтому оператор DISTINCT является встроенным. Опять-таки, это — одно нз наиболее важным различий между определениями таблицы и отношения. Соединение Оператор соединения используется, чтобы связать одну таблицу с другой вв основе соответствия значений столбца, обычно первичного ключа одаой таблицы с внешним ключом .другой. SELECT fieldname FROM tablenaniel JOIN ЬаЫепагаеЗ ON tablenameX.keyl - tablenamel.keyl Мне кажется, что эта операция также должна быть объявлена, используя более реляционный алгебраический стиль. Реляционный алгебраический стиль показывает, как ограничены возможности реляционных операторов по сравнению е гораздо более функциональным SQL-языком.
I JOIK tablenamel WITH tablenameZ OVER key! Это обозначение на самом деле намного более ясное, но делает точно ту же саму» вещь. Оно, конечно, работает только тогда, когда названия ключей одинаковы. Причина, по которой я показываю ато обозначение, заключается в том, что я хочу ясно дать понять, что JOIN в первом отрывке кода можно представить как отдельную таблицу. Далее рассмотрим влияние объединения двух операций JOIN, как в следующем запросе: И SBLBCT fieldname FROM - tablel JOIN t*ble2 ОЙ tablel. Jceyl - tableZ.keyl JOIN tabl*3 И ON table-2, key3 “ tables.key2 Первую операцию соединения можно представить следующим образом: (SELECT » FROM Tablel JOIN table2 ON tablel.key! « table2.keylj - derivedTablel Тогда мы можем переписать наш запрос следующим образом: SELECT FROM fieldname derivedTablel JOIN table3‘ ON derivedTablel.key! - table!.-:ey 2 Это все, насколько глубоко мы будем вникать в операторы соединения в атом разделе. Цель этого раздела просто ввести в курс работы оператора соединения и связи его с технологией соединения в SQL Server. В главах 11 и 12 мы снова рассмотрим эту концепцию. Произведение Одной из наименее полезных и наиболее опасных операций является операция произведения. Оператор произмдвяия берет две таблицы и строит таблицу, состоящую из всех возможных комбинаций строк по одной из каждой таблицы. В SQL-языке он обозначается выражением CROSS JOIN. SELECT fieldname I FROM tablenamel CROSS JOIN tablenaiBeS Для лучшего объяснения этой операттии рассмотрим следующее:
Table!: Fred Tablet' Sam Tabi el product Tablet Fred Sam Wilbur Bugs (умноженная на) Fred Bugs Ed Fred Ed Wilbur Sam Wilbur Bugs Wilbur Ed Вы можете видеть, что чивло строк, которое будет возвращено оператором произведения — число строк в Tablel, умноженное на число строк в ТаЫе2. Это числе может стать весьма большим, содержать двойные и бесполезные строки и может быстро задушить ваш сервер, так что оператор произведения используется не так часто. Объединение Оператор объединения берет в качестве входных две тавлиды одной и той же структуры и строят другую таблицу со всеми строками из обеих таблиц, но удаляя дубликаты: SELECT fieldname FROM tablenamel | UNION SELECT fieldname FROM tablename2 Table 1: Fred ТаЫе2: Fred — Tablel product Table2 Fred = Wilbur Wilbur (объединение c) Wilbw Babe Babe Самое время напомнить вам, что две входные таблицы оператора объединения должны иметь один и тот же тип, но ие обязательно они должны быть базисными таблицами. Таблица может также рассматриваться и как вторичная таблица, сформированная операцией набора, рассмотренного ранее. Пересечение Оператор пересечения очень близок к операторам объединения и присоединения. Оператор пересечения в значительной степени противоположен оператору объединения. Он использует две таблицы одаой и той же структуры, удаляет значения, которые не присутствуют в обеих таблицах одновременно, и воввращает результирующую таблицу, - Tablel: Jeb Tablet: Jeb Tablel intersect Tablel Jeb Silver Howie Carney Carney Billy Carney Bob
Пересечение непосредственно ве реализовано SQL-синтаксисе. Следующий фрагмент кеда показывает основной способ реализации пересечения в SQL, SELECT fieldname FROM tablenanel JOIN tablena«ie2 Ц ON tablenamel.fieldname - tablenameZ.fieldname Разность Разность — операция, которая используется весьма часто при реализации системы. Точно так же. как объвданение и пересечение, разность снова требует таблиц одной и той же структуры. Суть оператора разности заключается том, что мы хотим подучить каждое значение из одной тяблипы, которого на» в другой таблице. Это — в противоположность оператору пересечения, который удаляет значение, если оно не находится в обеих таблицах. Разность снова не имеет соответствующего ключевого слова в SQL. Она может быть реализована наиболее простым способом, используя оператор NOT IN, как показано ниже: I SELECT fieldname FROM tablenamel WHERE fieldname HOT IN (SELECT fieldname FRO’! tablenamelj Если вы программировали на SQL, то, вероятно, заметили, что вта реализация, возможно, не оптимальна, хотя > логическом смысле это — лучшая реализация. Хотя мы обычно не должны формулировать запросы на етадаи логического моделирования проекта БД, однако, если это потребуется, мы должны всегда использовать наиболее прямое представление запроса, который мы хотим сформировать. Деление Оператор деления — один из странных операторов, который вы, вероятно, никогда не будете использовать. Он использует два. отношения, одно — с единственным столбцом, другое — с двумя столбцами, и возвращает таблицу значений, которые соответствуют (в столбце, который определен) всем значениями в отношении с единственным столбцом. Нет никакого определенного способе осуществить это способом, который будет ясен в SQL, так что мы опустим и© здесь ив нашего обсуждения, поскольку оператор не является жизненно важным для большинства архитекторов ВД и SQL-программистов. Другие операторы Дополнительные операторы могут быть разделены на две категории — типы корректировки (INSERT, UPDATE, delete) н дошишительные поисковые типы. Мы не будем в этой главе обсуждать операторы корректировки, поскольку они очень близки к тому, как SQL реализует их, а также имеют очень небольшую возможность логического варьирования. Мы будем обсуждать их в некоторых деталях в главе 12. Так же как и поисковые операторы, есть другие реляционные операторы, определенные в течение почти тридцати последних лет, начиная с формулировки рассмотренных выше восьми основных операторов. Фактически все когаанды DML-языка (Data Manipulation Language — язык манипулирования данными — ЯМД) в SQL Server являются или
результатом, или расширением этих основных операторов. Мы должны также обратить внимание, что DDL-язык (Data Definition Language — язык определения данных — ЯОД) в основном неиольэуетея для определения яяших объектов, и в то время как может и не быть никакого эквивалента реляционного оператора, любой реляционный язык должен осуществить некоторую форму DDL, чтобы строить объекты для DML. Я ограничил пределы возможностей книги этими восемью исходными операторами. Цель данного раздела, посвященного реляционным операторам, состояла на в том, чтобы заменить или даже дополжит* различные учебники по згой теме, а, скорее, дать представление, которое поможет нам разобраться е основами того, откуда произошли SQL-операции. Понимание основных принципов SQL поможет нам при логическом проектировании наших ВД и написании кода. Отношения Мы уже упомянули кощепцию первичного ключа и внешнего ключа. Теперь поместим их рядом и посмотрим на объект, который они формируют, —отвошеме. Отношения — его то, что делают таблицы, которые вы успешно создали, заменяя повторяющиеся группы связью таблиц, Однако имеется один момент, который дог должны учесть 1фк этом. Типы отношений,, которые-мы будем обсуждать в «ом разделе, не сводятся только к форме, когда на первичный ключ одной таблицы ссылается другая таблица через внешний ключ. Если это так, то независимо от того, как все отношения будут в конечном счете физически выполнены при реализации БД, логически может потребоваться несколько отношений "первичный ключ — внешний ключ", чтобы обеевечить простые логичесяие отношения. В этом разделе мы обсудим тины логических отношений и оепмт. обсуждение физической реализации на более позднее время. Отношения на этом этапе могут выть разделены на два основных типа, смысл которых нам нужно понять: □ бинарные отношения: □ небинарные отношения. Различие между этими двумя типами заключается а числе таблиц, обес печив ающ и х отношение. Бинарные отношения включаю® две таблицы, в то время как небинарные отношения включают более чем две таблицы. Это может показаться маленьким различием, но на самом деле это не так. SQL и связанные с атам отношения ограничены бинарными отношениями, в то время как в реальном мире нет никаких таких ограничении. Когда вы проектируете ВД, то должны помнить и уметь представить каждую из возможных ситуаций. Когда мы будем рассматривать моделирование данных в главе 5, то овсудам, как представить каждую из них в модели данных. Бинарные отношения Бинарные отношения — отяопижия между двумя таблицами. Большинство отношений, с которыми мы обычно имеем дело, подойдет под ату категорию. Число порожденных строк, которые могут участвовать с каждой из сторон отношения, называется мощностью. В этом отношении мы рассмотрим различное число элементе® бинарных отношений.
Отношения "один к п” • Отношения "одаа к п" представляют сйой класс отношений, г^е одна таблица передает свой первичный ключ другой таблице в качестве внешнего ключа. Таблица, которая содержит первичный ключ, известна как таблица-предок, а таблица, которая получает и использует его как внешний ключ, — как таблица-потомок. Эта терминология предков-потомков рассматривается только в отношении между даумя конкретными таблицами. Потомок может иметь много предков, а предок может иметь много потомков. Отношение "один к п“ — наиболее общий тип отношения, с которым вы будете иметь дало. Однако имеется несколько различных возможностей для мощности этого отношения. Можно выделить следующие варианты: □ Отношения "один к одака^": мощность отношения "один к одному" означает, что для любого конкретного предка может существовать только единственный потомок. Примером этого типа отношений могло бы выть отношение House (дам) — Location (расположение), как изображено на рисунке ниже. Очевидно, что дом может находиться только в одном месте. Обсуждение того, почему все это не расположено в одной таблице, отложено на более позднее время. Houaeld (идентификатор дома) House Туре (тип дома) 1001 1 1002 2 1003 - - 1 1004 3 1005 4 Houseld (идентификатор дома) location (расположение) 1001 Database Drive 1002 Administration Avenue 1ЖЙ SQL Street 1004 Primary Key Place 1005 Business Rules Boulevard □ Отношения "один ко многим": отношение "один ко многим" является наиболее важным типом отношения. Для каждого предка может выть неограниченное число записей-потомков. Примером отношения "один ко многим" могло вы выть отношение "State (государство) — Address (адрес)", как локавамо на рисунке ниже. Очевидно, имеется много адресов для одного государства, хотя конкретный адрес может быть только в одном государстве.
State Id Мо.(идшт*ф«атор штата) State (штат) л г ^ya3g С О; 3 Saanais* Address (адрес) J—П1Т-ГТ ГТТЧ1ГГ Unb.olwttron. (we&MtiSsaise: 1 402 Database Drrve 1 1 1 1222 Adfr nstra'.or Aver ее 2 1 2 2 1 а 2i6 Cardinality Close 1 1 г 393 Ser ver Street 3 2 3 WQuery Quty 2 J 3 2'33 Primary Key Ptace 4 2 □ Отношения ’ один точно к и": фактически отношение "один к одному" — просто частный случаи этого типа отношений. Это редао используемый тип отношений, но в подходящих случаях он может использоваться. Например, ои мог бы выть использовал, если пользователь может иметь только два адреса электронной почты. Следующий рисунок показывает, как отношение мощности "един к даум" могло вы использоваться, чтобы обеспечить отношение "User (пользователь) —> E-mail (электронная почта)”. □ Рекурсивные отношения; рекурсивные отношения — это те отношения, когда предек и потомок — одна и та же таблица. Этот вид отношений — то, как можно реализовать дерево, используя SQL-конструкции. Если вы реализуете рекурсивное отношение мощности "один к даум", то можете создать древоподобную структуру данных, Классический пример рекурсивных отношений — спецификация материалов. Возьмем автомобиль. Составные части и он сам могут ряпгдгятринятьг-я как части стоимости для изготов1гтеля, и каждая из его компонент, которая также имеет свои части, представляет часть, которая входит в целое. Некоторые из этих компонент также составлены из частей. В рассматриваемом примере автомобиль можно представить составленным рекурсивно из частей автомобиля и всех их составных частей.
Гл эва 3 Отношения ’нногие ко многие" Другой тип бинарных отношений (фактически второй наиболее общий тип отношений) — отношение “многие ио многая". В отлвчет от случая, когда имеется единственный предок и один или большее количество потомков, здесь может быть больше чем один предок и потомок. Это невозможно осуществить, используя только дае таблицы, но ш оставим реализацию отношений “многие ко многим" до части книги, связанной е физическим моделированием, и- главы 10. Примером отношения “многие ко многим" может быть автомобильный дилер. Возьмем любую конкретную модель автомобиля, которая продается многими различными автомобильными дилерами. -Далее возьмем одного конкретного автомобильного дилера. Он, в свою очередь, продает много различных моделей автомобалей. Небинарные отношения Небинарные отношения включают в отношение больше двух таблиц. Это бывает гораздо чаще, чем можно было бы ожидать, Например: Жгох сввОжоет книга.ии продавцов книг, которые обеспечивают их продажу. Оно называется тройным отношением, потому что включает три таблицы. Когда мы имеем дело е такими отношениями, то разбиваем их на три таблицы с двумя или большим количеством отношений, □ Wrox снабжает книгами. □ Продавцы книг* продают книги , □ Продавцы книг снабжаются мадятялытюм Wrox. Однако из этих трех отношений вы не можете точно вывести исходное тройное отношение. Все. что вы можете вывести, это: , □ Wrox снабжает книгами кого-то. □ Продавцы книг продают книги, которыми их снабжает какое-то издательство. □ Wrox обеспечивает чем-то продавцов книг. Из этих отношений вы не можете точно вывести исходное утверждение, что Wrox снабжает книгами продавцов книг. Это — обычиая ошибка, которую делают многие архитекторы данных. Исходное отношение будет, вероятно, иддатифиттроа^ига яя ста дии проектирования, а затем яе будет реализовано должным образом, когда вы начнете разрабатывать отношения. Надлежащее решение ятрго вида задач и. состояло бы в том, чтобы иметь таблицу для издателей (Wrox), таблицу для изделии (книги) и таблицу для магазинов (продавцы книг). Тогда вы имели вы таблицу с тремя бинарными отношениями, по одному на каждую из этих таблиц. Это решает проблему (пока). В главе 7 на основе расширенных правил нормализации мы будем иметь дело с проблемами, которые возникают, когда вы должны хранить отношения с более чем двумя таблицами.
Последнее замечание относительно реляционной/SQL терминологии В этой книге мы будем придерживаться терминологии SQL-стиля кроме случаев, где это не имеет смысла. Важно понять основные принципы реляционной теории, однако не нужно смущаться и тому, кто ве прочитал му главу или серьезно не изучал ВД. SQL-терминология может и не быта совершенной (и действительно, это так), и так думает вольншиство программистов, даже те, кто являются приверженцами реляционной теории. Определения существенных понятий Прежде чем завершить эту главу, описывающую фундаментальные понятия БД, следует рассмотреть пару дополнительных понятий. Они будут очень важны позже в процессе проектирования (н, возможно, более ясными), но представление о них важно дата в атой главе. Функциональная зависимость Термин функциональная зависимость является одним из тех терминов, которые выглядят более сложными, чем есть на самом деле. Это — фактически очень простое понятие. Если мы определяем функцию для какого-то одного значения (назовем его Value 1). а результат этой функции всегда одно и то же значение (скажем, ValueZ), то ValaeZ функционально зависит от Valuel. Это довольно простое, но все же важное понятие, которое нужно представлять. Правильно реали зов а н и ^функциональные за а ион мое г и —— очень полезные вещи, в то время как неподходящие функциональные зависимости являются основой многих проблем ВД. Существует один дополнительный запутывающий термин, который связан с функциональной зависимостью. Этот термин —детерминант, который может быть определен как "любой атрибут или комбинация атрибутов, от которых функционально зависит любой другой атрибут или комбинация атрибутов". Так, в нашем предыдущем примере, Valuel можно было бы рассматривать как детерминант. На ум приходят два примера: □ Рассмотрим математическую функцию типа 2 * X. Для каждого значения X будет получено конкретное значение. Для 2 мы получим 4, для 4 мы получим 8. Каждый раз, когда мы помещаем в функцию значение 2, мы всегда будем получать 4, так что в этом случае 2 функционально определяет 4 для функции 2 * X. □ В более связанном с ВД примере рассмотрим серийный номер изделия. Из серийного номера может быть получена дополнительная информация лива номера модели и; других конкретных характеристик изделия. В этом случае серийный иомер функционально определяет ряд конкретных характеристик. Если это вам кажется анакомым, то потому, что любой ключ таблицы функционально определяет другие ее атрибуты, и если вы иместе дм ключа, один из которых — первичный ключ, а доугой — вторичный ключ, то первичный ключ функдаонально определяет вторичный ключ я наоборот. Это понятие будет самым зажным при обсуждении нормализации в главе 6. ед
Зависимость с несколькими значениями Интуитивно менее понятной является зависимость с несколькими значениями. В нашем г предыдущем разделе мы обсу:ндали футшциогшльпую зависимость в случае едипетпешюго аначеши. В качестве иллюстрации зависимости с несколькими значениями рассмотрим факт, что эта книга выла написана одним автором, так что, если бы мы определяли функцию автора этой книги, мы каждый раз должны были бы иметь единственное значение. Однако если бы существовала функция технического редактора, ояа возвратила бы много значений, тая как у этой книги несколько технических редакторов. Следовательно, технический редактор для книги имеет несколько значений. Мы снова столкнемся с понятием функциональной зависимости позже, в главе в. Резюме В этой главе мы проскочит тридцать лет исследований всего с помощью нескольких страниц. Мы определили многие из терминов и понятий, которые являются очень распространенными как в нашей нынешней корпоративной прагматической культуре, так и в кругах "чистых" исследователей. Мир нуждается в обеих из этих групп, чтобы работать вместе,- Мы также посмотрели на некоторые основы построения реляционного языка программирования SQL. В следующей таблице мы установим соответствие между общими терминами ВД SQL Server и их синонимами из реляционной теории и, в меньшей степени, терминологией логического проектирования: Общий термин Синоним Таблица Столбец Строка Допустимые значения, которые могут быть размещены в столбце, учитывая тип данных, ограничения и т. д. Число строк Число столбцов Сущность (конкретно для логического проектирования), отношение (чаще в теоретических дискуссиях) Атрибут Кортеж Домен Мощность Степень Моя искренняя надежда — что данная глава представит эти темы на уровне технической конференции, что, по крайне* мере, даст вам достаточное теоретическое понимание, чтобы сделать проектирование ВД понятным, Или, что лучше, стимулирует вас пойти и прочитать больше о предмете, чтобы вы полностью поняли всю ту информацию, которую я пробежал слишком быстро. Конечно, мое реальное опасение — что сам Е. Ф. Кодд прочитает мою книгу и придет в мой дом, чтобы отругать меня за то, что, потратив года и проштудировав большое количество страниц хорошо обдуманных записей и исследований, я превратил их в двадцать страниц или около втого, В этом русле я надеюсь, что вы будете эту тему изучать и далее, так как она позволит вам стать хорошим архитектором/программистом БД. Сделав краткую теоретическую остановку во время путешествия в страну ВД, мн возвращаемся к изучению нашей темы, поскольку действительно начинаем сам процесс проектирования.
—-J1 Сущности, атрибуты, отношения и бизнес-правила Введение Теперь, посмотрев на основные блоки здания БД, мы должны начать идентифицировать эти блоки здания в рамках предварительной информации, которую мы собрали в главе 2. Я допускаю, что такой подход выглядит излишне осторожным, но ио — "лучшая практика", однако многие квалифицированные программисты, вероятно, будут иметь свой план. Я так же на это смотрю; однако результаты, которые я получаю, всегда гораздо лучше, когда я выполняю эти предварительные шаги перед началом проектирования новой ВД. Чем больше мы будем следовать атому пути при проектировании, тем лучше будет результат. Методы, описанные в этой главе, помогут вам избежать опасности увеличения затрат на обслуживание проекта, уменьшить частоту, с которой вы должны будете потом возвращаться. чтобы изменить структуры и код, и вообще уменьшать риск неудачи. . В главе Я, где описывается стадия сбора информации, мы собрали массу информации, которая в настоящий момент загромождает ваш стол. Ваяй главе добавим к этой информации еще структуру и затем взглянем на все интегрально. Мы пройдем все — идентификацию н назначение начальных названий таблицам, отношениям, столбцам н бизнес-правилам — конечно же, при этом мы консультируемся с нашими пользователями для получения любой дополнительной информации. Закончив этот процесс, мы будем иметь основную документацию, которая представлена все еще в формате, когда можно делиться информацией с пользователем без бремени технических терминов и диаграмм (которые мы начнем разрабатывать позже).
Отказ от выбора окончательной структуры на ранних стадиях Для тех читателей, кто плохо знаком с проектированием ВД, или кто даже ив создал ни одной таблицы, принципы, рассмотренные в этой главе, должны выполняться, чтобы гарантировать лучший результат для полученного проекта. Если вы уже знаете, как создать таблицу, то могли бы подвергнуть сомнению упрощенный характер того, как мы рассуждаем в этой главе. Вы уже можете иметь некоторые таен того, какие таблицы следует разбить и на какие составные части. Од ня ко при выполнении таких действий на ранних этапах процесса проектирования, что может казаться выгодным для сокращения времени разработки, вы заплатите за это в полной мере, когда должны будете возвратиться для корректировки ВД, вставляя, например, столбцы, потому что вы пренебрегли необходимостью потратить время для рассмотрения вашей стратегии с клиентом. Не забудьте также продолжать корректировать вашу документацию, так как лучшая в мире документация бесполезна, если она устаревшая. Как только вы создадите одну БД или две (или две сотни таких БД), то, вероятно, поймете, что очень трудно отказаться от досрочного выбора окончательной структуры. Когда вы слушаете клиента, объясняющего, что он хочет, то, вероятнее всего, представляете набросок структуры даже до того, как он выскажет все, что хочет; это будет почти вашей второй натурой. К сожалению, это — же лучшее, что можно сделать на агой стадии развития/организацви процесса проектирования ВД, - ' ’ Вы должны до поры, До времени игнорировать эти организационные поползновения. Независимо от того, с помощью каких ередетв вы фактически объединяете информацию (средств создания ER-диаграмм (ER-диаграммы — диаграммы сущностей и отношений), средств объектно-ориентированного представлевя структур, текстового процессоре или даже карандаша и бумаги), — со стремлением построить ВД яа этом этапе нужно бороться. Почему же? Ответ очень прост. В вашем рвении соврать воедино воздаваемую ВД, вероятно, будет пропущена ценная информация, не будут выполнены, по-видимому, и некоторые важные шаги, которые мы обсудим в следующих главах. На атом этапе вы должны ограничить себя только рассмотрением собранной документации для определения следующих моментов: □ сущности и отношения; □ атрибуты и домены; □ бизнес-правила. Делая это, вы, вероятно, завершите работу с намного меньшей грудой документации, возможно, представляющей десять процентов от первоначального объема, но будете все еще уверены, что не пропустили никакие важные данные. В этот момент вы можете гарантировать получение информации от пользователя до того, как начнете применять очень жесткую последовательность операций, рассмотренную в следующих трех главах, чтобы выполнить процесс разработки. Мы упомянули в главе 3, что с этой главы и далее будем использовать ориентируемые на рсялизапию термины, чтобы описать объект, когда это имеет смысл. В этой главе мы используем термины "Сущности" и "Атрибуты’’, поскольку будем искать объекты, которые никогда не могут появиться как физические структуры. Мы начнем использовать ориентируемые яа ремизацию термины, как только станем переходить я структурам, напоминающим то, что обычно называется таблицами и столбцами.
Простой пример Проиллюстрируем то, что вы только что прочитала, следующим примером, который йог легко оказаться фрагментом записей реального процесса проектирования. 3tkaA<ht kgnt^wt. вш, фАная поНф^швш» Ланя, аЫнв фкш ! ш шк/а м ляй» Ли №ю^я&ф!><я узкЛиШ n&u^№tt atatmoA О»няк&& гфоЗякк wfag ittff, гю м^икте ~-ж щ на iet «ни Л^ийииЬии Cteiui-'pahiyffbt '^шяз ^«Аж, ив swAw Лааювк U mauiko ЗЛЛЛ Лажи ЗЛ>^ *яя tMg ЛЫфяак (nes нмЫда- к^йЗшнывс nfafos*}. ЛкАа пЛчентия Жпд-ф1ктфм Аю ЗМ^шашфЛш. .Вда Люия яяфЛяяя'и a doaHa £ i/шфе нфяк. Лужм шчшяая в кивикйа£&я hniami &Ый& ~ таярп+нж f^hfe и *риг и i|te» я^й^мни! веЬм. иаюиШяя фвк мш ткИяр9н№/ю шкя} iui фанЛуяи Жшж inahp w-wm МЛ^ю Лить Ли кявяЛя^ якав M^t Аам яа&ш kunumkutf $ Мл4и»г В каждом из последующих разделов, мы будем просматривать эту документацию, чтобы выявить нужную информацию. Звучит достаточно просто, в? Это, действительно, намного легче, чем могло бы показаться. Определение сущностей Сущности, действительно, одни из наиболее простых для выявления объектов, когда вы просматриваете документацию. Оии обычно представляют людей, места, объекты, идеи или вещи, которьге грамматически выступают как существительные. Это не означает, что сущности отряжают только реальные вещи. Ими могут быть также абстрактные концепции, например, "Новые изделия" является абстрактной концепцией, которая не является физической, но представляет удобный сносов группировать диапазон изделий. Фактически, более разумами определением того, что такое сущность, может быть то, что она используется для размещения всей наглядной информации, необходимой для полного описания отдельного человека, места, объекта или идеи. Например: □ Человек: может представлять студента, служащего, водителя к т. д. □ Место: город, здание, дорога и т. д. □ Объект: деталь, инструмент, электронный блок и т. д. □ Идея: документ с требованиями, группа (подобно группе безопасности для приложения), журнал действий пользователя и т. д. Обратите внимание, что имеется йаложвш» нескольких из категорий {например, здание — "место" или "объект"). Вам редао будет требоваться дискретно идентифицировать, что
представляет собой сущность — человека, место, объект или идею. Однако если вы можете разместить ее в пределах конкретной группы, аю может помочь назначить некоторые атрибуты, например "Имя* для человека или "Адрес” для здания. Для тех, кто имеет опыт работы с реляционными ВД, будет ясно, что когда сущность реализуется в фактическую таблицу ВД, она будет, вероятно, очень отличаться от начальных сущностей, которые вы определили. Важно не волноваться относительно этого на данной стадии процесса проектирования — мы должны постараться не заниматься серьезно данный момент возможной реализацией ВД. Причина, почему мы заинтересованы различать людей, места и объекты — то, что это помажет определить некоторую основную информацию, которая потребуется позже, когда начнем заниматься в давиой книге реализацией. Это также поможет нам быть уверенными, что мы имеем всю документацию, необходимую для описания каждой сущности. Люди Почти каждая БД должна будет хранить информацию относительно некоторото вида сущностей, представляющих людей, Волышгастао ВД будет иметь, по крайней мере, какое-то упоминание пользователя. В то время как не все пользователи — люди, о пользователе почти всегда думают как а человеке, хотя пользователем могла Вы быть корпорация иди даже машина, наподобие сетевого принтера, Что же касается реальных людей, в отдельной БД может потребоваться хранить информащио о многих людях. Например, ВД школы может иметь в качестве сущностей учаищхся, учителей и администрацию. Может также потребоваться, чтобы только одна таблица представляла все три категории. Это — вопрос, который мы рассмотрим поподробнее в более поздаяж примерах. В нашем примере можно выделить две сущности людей — "Контакты" и "Заказчики": нузрш тчесаш» каффм/ию о ЬюяЬмх iw МиЛда ЛАкЯжЬ ... ^'^нля jbwwfarf ... Информация о партнерах и заказчиках, которую нам нужно хранить, должна, одаако, быть описана, и мы на-этой стадии процесса документирования не можем звать, действительно ли они будут рассматриваться как отдельные сущности. Мы рассмотрим различные атрибуты партнеров и заказчиков а разделе этой главы, касающемся атрибутов. Места Существует много разл ичных типов мест, относительно которых пользователи пожелают хранить информацию, В нашем примере записей имеется одна очевидная сущность, связанная е местом: нЛ)яж ш склада t ркф G&ffKcvm внимание, что партнеры и заказчики, очевидно, имеют почтовые адреса, определяющие их местоположение.
Объекты Объекты относятся, прежде всего, к физическим предметам. В натпем примере мы имеем три различных объекта: fcasafav mu гфмм ютп сйФ.вд ш м Изделия, офисное оборудование и мебель — объекты, но заметим, что мебель и офисное оборудование — также изделия. Однако вас не должно шокировать, что изделия, офисное оборудование и мебель будут иметь очень разные атрибуты. Можно ля объединить эти три объекта в одну абстрактную сущность, называемую изделиями, или будем рассматривать их как три отдельные сущности, будет полностью зависеть от того, е какой целью вы разрабатываете конкретную ВД Идеи Нет никакого правила, требующего, чтобы сущности были реальными объектами иля даже физически существовав. Ня mot стадии разработки мы должны будем рассмотреть информацию, которую пользователь хочет хранить об объектах, которые не соответствуют уже установленным категориям "людей", “мест" и "объектов" и которые могут быть или не быть физическими объектами. Документы Для многих термин "документы" обычно асооциируется с осязаемыми листами бумаги, содержащей информацию, которую клиенты должны отслеживать, Это может выглядеть как спор о мелочах, но это не то же самое. Что, если мы сделаем копии листов бумаги? Означает ли это, что имеются даа документа, или оба они — тот же самый документ? В нашем примере мы действительно просто ссылаемся на большое число листов бумаги: ... пЛвшвЯся ... Счета-фактуры — листы бумам, которые отправляются заказчику после поставки изделий. Однако мы только знаем, что счета-фактуры напечатаны физически на бумаге, некогда они отправлены или даже как они посланы, на этой стадии неизвестно. На этой стадии мы только идентифицируем сущности и двигаемся дальше; опять мы выдвигаем предположения относительно того, как данные будут использоваться позже в этом процессе. ... Ля флюцйуии шШффИешЛ Здесь мы имеем тип документа, который не написан на бумаге — сообщение электронной почты. - Другие сущности На этой стадии разработки мы должны будем рассмотреть информацию относительно сущностей, которые пользователи хотят хранить и которые не соответствуют уже установленным категориям "людей", "мест", “объектов" и "документов".
Протоколы ревизий и журналы БД Типичная сущность, которую вам нужно будет определить — протокол ревиаии или журнал ВД. Это — не обычная сущность, в которой не хранятся никакие данные пользователя и, по существу, должна вообще рассматриваться на физической стадии проектирования. Единственные виды сущностей, которые мы рассматриваем на этой стадии — это те, о которых пользователь желает хранить даивые. Аналогично и операторы, наподобие следующего, не нужно рассматривать на этой стадии, а следует оставить до реализации: Лобш мЛчеявкмя Ашо-фЛя^Л ^лжны Ан» События Сущности-события обычно представляют собой глаголы или действия. .... мшвЛкм Зшкжя нвмЛшше, hk ж'Ж _ Записываются ли поставки в какой-либо физический документ, на данном этапе неясно; выражение лишь указывает, что событие имеет место. Эти события важны, и пользователи захотят вести их список или формировать данные, связанные с возникновением событий. В нашем примере, может быть, мы будем документировать события после того, как они произойдут, так что мо может вылиться в протокол сделаяных поставок. Другим типом событии для вас могут быть показания счетчика, погодные данные, измерения приборов и т. д. Записи и журналы Последний из типов сущностей, которые мы исследуем иа этой стадии — запись или журнал действий. Заметьте, что я имею в виду записи, не находящиеся в ВД. Это может быть любой вид деятельности, при котором пользователь мог вы предварительно сделать записи на бумаге. В нашем примере пользователь хочет хранить записи о каждом сделанном контакте с заказчиком. ... .ж^г АЛ» ууркал с -ЛАЛЛжя, Это другой тип сущностей, который подобен контрольному протоколу, но потенциально он будет содержать больше информации, — такой как замечания относительно контакта, а не только то, что контакт имел место. Список сущностей Пока мы определили следующий список предварительных сущностей.
Сущность Описание Контакт Люди, выступающие в качестве заказчиков, я е которыми следует установить контакт Заказчик Организация или конкретное лицо, которым продаются изделия, офисное оборудование и/идк мебель Склад Место, где размещаются товары для продажи Поставки События, когда проданные товары поставляются заказчику Изделия Товары для продажи Офисное оборудование Тив изделия Мебель Тип изделия Счет-фактура Физический документ, который отправлен заказчику, чтобы запросить оплату за проданные изделия Подтверждение Электронный документ (возможно, это единственный размещения изделия формат), который позволяет сообщить заказчику, что заказ размещен Журнал Распечатка всех контактов Бухгалтерский журнал Фактическая запись контакта е заказчиком Замечание для физического моделирования: регистрировать любые изменения счетов-фактур Описания основаны на фактах, которые мы тщательно выбрали из исходной документации. Обратите внимание, что в оииеании коитажта мы не стали делать никаких предположений относительно того, как используются данные, то время как полностью описаны главные моменты, основанные на том, что Выло предварительно согласовано. Теперь мы имеем список всех сущностей, которые были определены в нашей предыдущей документации. На это# стадии мы постоянно просматриваем то, что сказал нам клиент. Когда же мы начинаем анализировать то, что выло определено, то можем заняться деталями, которые клиент нам не дал. Вмеси» того чтобы заполнять бреши предположениями и догадками, нам нужно (в идеале) подготовить список вопросов и передать, их деловому аналитику, который повторно посетит клиента, чтобы извлечь большее количество информации: □ У вас имеется только один склад? □ Ваши заказчики — отдельные лица или большие сообщества? □ Имеются другие типы изделий, которые вы продаете? Или мебель и офисное оборудование настолько важны, что вы должны хранить специальную информацию относительно них?
Определение атрибутов и доменов Основная причина использования здесь термина "атрибуты" вместо термина "столбцы* заключается в том, что мы будем искать влементы, которые идентифицируют, составляют, и описывают сущность, которую вы пытаетесь представить: либо, выражая это в компьютерных терминах — свойства сущности. Например, если сущность — "Человек", атрибуты могли бы включать "Номер водительских прав", "Номар социального страхования", "Цвет волос", "Цвет глаз", "Вес", "Супругу" ("Супруга'’), "Детей", '’Почтовый адрес" и "Адрес электронной почты". Каждая ив этих характеристик служит, чтобы представить какую-то сторону сущности. Однако на нашего определения того, что. должен хранить столбец, мы знаем, что это должна быть еданая часть информации, хотя для атрибута такого требования нет. Идентификация, которая может связать атрибуты с сущностью, требует различного подхода, исходя из определений самих сущностей. Атрибуты часто находятся как прилагательные, используемые для описания сущности, которую вы предварительно нашли. Поскольку информация о домене данных обычно определяется в то же самое время, когда и атрибуты, мы также посмотрим и на них. Идентификаторы Причина, почему мы начинаем с идентификаторов, заключается в том, что они являются наиболее важными. Каждая сущность должна иметь по крайней мере один идентифицирующий атрибут или набор атрибутов. Вез атрибутов нет никакой возможности далее в процессе определять различные объекты. Этими идентификаторами является то, что мы определили как ключи в главе 3, В вышеупомянутом примере один такой идентификатор показан следующим образом; Они змиШиШ яомфя, сиеятоЛ, Почти для каждой сущности, которую вы разрабатываете на этой стадии проектирования, легко найти какой-либо идентификатор. Причина этого кроется в том, что на этой стадий вы имеете дело с сущностями, которые легко выбрать, потому что они имеют естественные идентификаторы. Важно также выть уверенным, что то, о чем вы думаете как об уникальном элементе, действйтельво является уникальным. Взгляните на имена людей. Для нас они почти уникальны, но ив самом деле в Соединенных Штатах имеются сотни Луисов Давидсонов, а это не самое частое имя. Имеются тысячи, если не миллионы, Джонов Смитов! Вот некоторые общие примеры хороших идентификаторов; Q Для людей — номера социального страхования (в США.)} полные имена (не всегда совершенный идентификатор); или другие числовые идентификаторы (например, номера заказчиков, табельные номера служащих и т. д.). □ Для деловых документов (счета-фактуры, счета, формируемые компьютером уведомления) — они обычно определяются некоторым назначенным числом, когда посылаются другим организациям.
Q Для книг — ISBN-номера (сами названия яе уникальны). □ Для изделий на продажу — номера изделий (сами названия изделий ие уникальны), □ Для компаний, с которыми клиенты имеют дело — они обычно задаются номером закаачика/кдамиа для отслеживания. □ Для зданий — полный адрес, включая индекс/почтсвый код, □ Для почтовых корреспонденций — имя и адрес адресата и дата отправки. Имеется много других примеров, но в данный момент вы должны понять, что мы подразумеваем, когда говорим об идентификаторах, Вспомнив реляционную модель, которую мы обсуждали в главе 3, каждый экземпляр отношения (или строка, если вы желаете) должен быть уникальным. Определение уникальных естественных ключей для данных — самый первый шаг в реализации проекта. Некоторые из вдеитификаторов, перечисленных выше, не иогут гарантировать, что будут уникальными; например, в случае номеров компаний и деловых документов идентификаторами могли бы выть последовательные числа, которые не являются естественными ключами. Однако но многих случаях эти значения — разумные ключи: это означает, что и них заключена и другая важная информация. В большинстве случаев разумные ключи могут выть разбиты на части. В некоторых случаях, однако, данные, вероятно, ничего не будут вам означать. Возьмем следующий пример серийного номера изделия: XJV 10229392: □ X — тип изделия — телевизионная трубка; □ JV — подтип изделия — 32-дюймовая консоль; □ 102 — номер партии, означающий, что изделие было произведено в 102-ой партии; □ 293 — дань го да; □ 9 — поеледаяя цифра года; □ 2 — цвет. Разумные ключи имеют свою цель, во при логическом проектировании мы должны найти все части информации, которые в них входят. Описательная информация Описательная информация — один из самых легких типов для определения. Прилагательные, обычно описывающие предметы, которые были предварительно идентифицированы как сущности, довольно распространенны и обычно указывают непосредственно на атрибут. В на»ем примере мы имеем различные типы изделий: новые и подержанные. ... гфогЪяют покусанные мЛяня ... Опи Главное здесь заключается в том, что мы теперь идентифицировали возможный домен атрибута. В этом случае атрибут ~ "Тип изделия’'. а домен, вероятно, будет включать "Новый" и "Подержанный”. Конечно, возможно, впоследствии его может быть преобразовано в два атрибута — "Является новым" и "Возраст изделия" или, может быть, единствеяный атрибут — "Год изготовления".
В следующем примере мы видим, что должны будем иметь атрибут, который определяет кредитные способности заказчика: ... kek mb ЗаЬ/Лик дааш. eatt дау dofefatos (нет никаких кфеднтных проблем^ Как только будет выяснено, есть ли у заказчика проблемы с кредитом, мы определенно будем хотеть в этот момент задать вопрос, что нам нужно знать еще относительно того, как БД может облегчить процесс управления кредитоспособностью. С другой стороны, следующий пример: дает впечатление, что мы имеем сущность "Контакт", которая имеет атрибут “Номер телефона" с доменом, состоящим из значений “факс" и "голосовой". Здесь уместно вставить небольшое напоминание, что одним из главных различий между столбцом и атрибутом является то, что столбец является физическим представлением единственного алекента информации, размещввной в той же самой физической сущности Или таблице, в то время как атрибут может быть физ.гчески размещен и не в той же самой таблице. Указатели на расположение Указатели на расположение используются как сносей разместить что-либо, начиная от физического ресурса и кончая классификацией, а также чтобы дифференцировать и размещать значения в таблице. Пример указателя на расположение приведен ниже: £ct иЛамя аюфаЛииякя со chuAi S 1/енпфе ифода Здесь мы имеем местоположение склада. Склад может иметь только одам адрес, ток что это определенный указатель на расположение, Обратите внимание» что из этого фрагмента фразы не ясно, имеются ли другие склады. Одно очевидно, что мы имеем по крайней мере один склад, который находится в центре города. В следующем примере мы имеем четыре очень типичных указателя на расположение: ... шмеапт в контактах для гмядшо Sehfittka — телефонные иомефа (<№ и «a»ceferfj, я адфеса элеклфеннэб offset. Большинство сущностей, которые связаны с организациями, будет иметь много атрибутов — телефонных номеров, адресов и адресов электронной почты. В нашем примере мы имеем четыре атрибута для сущности "Контакт". Дальнейший обзор позволит лише определить все фактические потребности, когда дело дойдет до этой информации, но это — довольно хороший перечень тоге, что важно- для пользователя.
Вы можете задаваться вопросом, почему мы думаем об адресе как единственном атрибуте, когда вы хорошо знаете, что ''адрес" состоит из номера квартиры, улицы, города, штата, почтового индекса — нескольких атрибутов. Ответом будет то, что все они составляют адрео, что мы мыслим обо всех вих как об единственном атрибуте, и этого нам достаточно. В это будет вложено больше смысла позже, когда мы натаем процесс структуризации, но пока будет достаточно представлять, что когда пользователь видит слово "адрес" в контексте нашего примере, он думает о понятии адреса, используемого для задания физического местоположения. Таким образом вы можете избежать любого обсуждения того, как адрес фактически сформирован, не говоря уже обо всех различных форматах адреса, с которыми мы, вероятно, должны будем иметь дело, когда займемся физической реализаций атрибута "Адрес" позже в этой книге. Последний атрибут, который мы рассмотрим, вытекает из следующего предложения: ili-ieawhp^Ho uxaiMvn факс- нлн электронную noimy out врюниЗауин неЛяй^фИвннЛ.. Атрибут “Факс или электронная почта" относится к механизму поставки, даж того чтобы определить, что мы будем использовать для передачи подтверждения заказа изделия. Мы будем, конечно, хотеть иметь записи того, как и когда послали изделия заказчику. Следовательно, нам будет нужев атрибут, описывающий механизм поставки, в таблице подтверждения заказа изделия, Связанная информация В некоторых случаях (хотя и не в няшем примере) мы будем иметь связанную информацию, которая потребуется в виде атрибутов для наших сущностей. Некоторые примеры этого следующие: □ Дополнительные материалы — пусть где-нибудь вы имеете сущность, которая описывает квиту или какой-нибудь другой тип средств информации (предположим, Amazon.com); вы, вероятно, захотите внести в список дополнительные ресурсы, которые пользователь может также посмотреть. □ Контакты — мы уже рассматривали таблицу контакта, но контакт также представляет собоЛ. технически и атрибут сущности. Независимо от того, захотите вы или иет, чтобы контакт был отдельной сущностью на данном или более поздних этапах, окончательная ВД будет выглядеть оданаково, потому что процесс нормализации (главы 6 и 7) гарантирует, что атрибуты, которые на самом деле являются сущностями, будут соответственно реструктурированы. □ Web-сайты, FTP-сайты (ПТ (File Transfer Protocol) — протокол передачи файлов) или другие различные Web-ресурсы — вы должиы будете часто идентифицировать сущности "Web-сайт" или "URL-ресурс" (URL (Uniform Resource Locator) — у инфицировали ый указатель информнционного ресурса, фактически адрес в Интернете), которые определяются как сущности; такая информация могла бы быть определена и как атрибуты, Список сущностей, атрибутов и доменов Следующая таблица показывает сущности совместно с описаниями и доменами столбцов. Атрибуты сущности помещены е отступом в пределах столбца сущностей.
Сущность Описание Домен столбца Контакт Люди, выступающие в качестве заказчиков, и а которыми следует установить контакт =. Номер факса Номер телефона, чтобы посылать факсимильные сообщения заказчику Любой подходящий номер телефона Номер голосового телефона Номер телефона для голосовой связи Любой подходящий номер телефона Адрес Почтовый адрес контактного лица Любой подходящий адрес Адрес электронной почты Адрес электронной почты контактного лица Любой подходящий адрес электронной почта Заказчик Организация или кон кретное лицо, которым продаются изделия, офисное оборудование и/или мебель Номер заказчика Ключевое значение, которое используется для идентификации заказчика Неизвестный Надежность кредита? Сообщает нам, можно ли отправить заказчику изделия немедленно True, False Склад Место» где размещаются товары для продажи Нахождение Определяет, какой склад имеется в виду, — только один может быть в этот момент, так как определено только одно значение домена "В центре города" Поставки События, когда проданные товары поставляются заказчику Изделия Товары для продажи Тип изделия Определяет различные типы изделий для продажи "Подержанное", "Новое" Офисное оборудование Специальный тип изделия ив продажу Мебель Специальный тап изделия на продажу
Сущность Описание Домен столбца Счет-фактура Физический документ, который отправлен заказчику, чтобы запросить оплату за проданные изделия Подтверждение размещения изделия Электронный документ (возможно, это единственный формат), который позволяет сообщить заказчику, что заказ размещен Тип механизма передачи Определяет, как подтверждение заказа изделия передается заказчику "Факс''. "Электронная почта" Журнал Распечатка всех заказчиков Бухгалтерский журнал Фактическая запись контакта е заказчиком Замечание для физического моделирования: регистрировать любые изменения счетов-фактур Обратите внимание на использование термина "Любой подходящий". Возможности этих формулировок должны быть уменьшены, до разумной величины. Многие БД, которые хранят, телефонные номера и адреса, не могут справляться со всеми возможными, форматами, используемымие разных регионах мира, главным образом, потому что ото не такая простая задача. На этой стадии мы определили список сущностей и атрибутов, которые нам удалось выделить. Заметьте, что при этом процессе мы все еще ве начали добавлять что-нибудь к проекту. Интересно выло бы также обратить внимание, что мы имеем документ почти в страницу, а всего-навсего проанализировали два маленьких параграфа текста. Когда вы вто сделаете в реальном проекте, окончательный документ будет намного больше, и в документации будет, вероятно, весьма немного избыточности. Вы будете вез сомнения определять нужные данные лучше, чем мы делали йа предыдущих немногих страницах! Отношения между таблицами Наиболее важные решения, которые вы будете принимать стноситвльио структуры БД, будут обычно связаны е отношениями. " Отношения "один к и" В любом отношении "овин к а" (то есть, "один к одному" иди "одан ко многим") таблица, которая является таблицей "один", рассматривается как предок, а "п" является потомком или потомками.
Отношение "один к и” часто используется при реализации, во редко встречается на ранних этапах проектирован и я БД. Причина этого заключается в той, что большинство естественных отношений, о которых сообщит вам пользователь, оказывается отношением "многие ко многим". Имеются два известных типа отношений "один к п", которые мы должны обсудить. Они ва самом деле весьма просты, и опытный проектировщик ВД выявит их немедленно. Отношение "имеет" Основной специальный тип отношения — отношение “имеет”. Оно названо так, потому что таблица-предок в отношении имеет одну или большее количество сущностей-потомков, используемых как атрибуты родителя. Фактически, отношение "имеет" — способ задания атрибута, который часто используется неоднократно. Некоторые примеры: □ адреса: □ номера телефонов; □ дети. В разделе с нашим примером мы имели: ... помета» о контактах для Закд&нкл ... В этом случае мы имеем сущность "Заказчик", которая имеет одного или большее количество контактов. Вы будете встречать этот тип отношения неоднократно; овратите также внимание, что эта является основой для решения, получаемого при венольаовании четвертой нормальной формы, которую мы рассмотрим в главе в/ Другой пример отношения "имеет" приведен в следующем примере: ... для конпмклюб, я. к от мм/я ttam окунал ketaiafaiot с- В этом случае партнер имеет несколько вхождений в журнал. С другой стороны невероятно, что вход журнала будет связан со многими партнерами. Отношение "тлеется" Другой специальный случай отношения "один я п" — отношения "является". Суть отношения "является" заключается в том, что сущность-потомок в отношении расширяет сущность-предка. Например, автомобили, грузовики, железнодорожные средства — это все типы транспортных средств, так что автомобиль является транспортным средством. Мощность такого отношения всегда “один к одному", поскольку сущность-потомок просто содержит более определенную информацию, которая определяет это расширенное отношение. Причина для этого вида отношений концептуальна. Может быть некоторая информация, которая является общей для каждой сущности-потомка (размещенная как атрибуты сущности-преда*)» Другая информация является специфичной для каждой конкретной сущности-потомка (размещенная как атрибуты сущности-потомка).
В кашам примере, мы имеем следующую ситуацию: ... пМтоя твффаюшй иЛми, тйм офнсное но nakfa и ме/м. В этом примере мы имеем сущность "Изделие", которая является достаточно общей. Сущность "Изделие” вряд ли будет содержать много информации относительно того, что представляет собой офисное оборудование, или из чего сделай предмет мебели. Это приближаете» к концепции наследования в объектно-ориеитированнои БД. Обратите внимание: отношение "является" несколько похоже на тивмжоёс в обье-ктнорриецтирован-яом программировании. В реляционных БД нет никакого понятия наподобие подкласса, хотя ото может измениться в следующих SQL-епецшрикациях. Мы обсидим эту тему дальше, когда приступим к моделированию ВД. Отношения "многие ко многим” Отношение "многие ко многим" используется гораздо более часто, чем вы могли бы представить. Фактически, поскольку мы начинаем детализировать наш проект, то будем использовать его для большей части наших отношений. Однако на данной стадии будет распознано только очень немного отношений "многие ко многим". В нашем примере имеется одно такое очевидное отношение: ДйЬжЙжАя, Ьявфмм ия гфюдтж -щкДеанине нЗовлня ... Если мы используем простое отношение "один ио многим", то были бы привязаны либо к одному заказчику, который приобретает разные изделия, или к тому, что каждое изделие может быть приобретено единственным заказчиком. С другой стороны, при таком отношении одно изделие может быть продана многим заказчикам, но каждый заказчик может приобрести только единственное изделие. Очевидно, что ни один иа этих вариантов не реалистичен. Многие заказчики могут приобрести многие изделия, и многие изделия могут быть проданы многим заказчикам. Список отношений Давайте посмотрим на документ, с которым мы работаем, еще раз, на сейраз удалив атрибуты, добавленные в предыдущем разделе, чтобы сэкономить место и сделать более ясным то, что мы сделали. 4 180
Сущность Описание } Контакты Люди, выступающие в качестве заказчиков, и с | которыми следует установить контакт Имеют бухгалтерский Для определения, когда они контактировали по журнал адресу заказчика Заказчики Организация или конкретное лицо, которым продаются изделия, офисное обррудтамше и/или ме*5ель Приобретают Заказчики приобретают изделия у клиентов изделия Имеют контакты Разместить имена, адреса, номера телефонов и т. д. людей по адресу заказчика Склад Место, где размещаются товары для продажи Размещают товары Товары размешаются на складе для продажи для отправки заказчикам Поставки События, когда проданные товары поставляются заказчику Изделия Товары для продажи Могут выть проданы Заказчики могут купить любые из продаваемых заказчикам товаров Офисное оборудование Тип товара Является товаром Офисное оборудование просто является расширением понятия изделия, возможно несущим большее количества информации Мебель Тип товара Является товаром Мебель просто является расширением понятия изделия, возможно несущим большее количество информации Счет-фактура Физический документ, который отправлен заказчику, чтобы запросить оплату аа проданные изделия Подтверждение размещения Электронный документ (возможно, это единственный изделия формат), который позволяет сообщить заказчику, что заказ размещен Журнал Распечатка всех контактов Бухгалтерский журнал Фактическая запись контакта с заказчиком Замечание для физического моделирования: регистрировать любые изменения счетов-фактур
Определение бизнес-правил Бизнес-правила могут выть определены как совокупность операторов, которые управляют и формируют деловое поведение. В зависимости от методологии организации данных, ати правила могут быть в форме помеченных списков, простых текстовых диаграмм или других форматов. Для наших целей мы прежде всего будем рассматривать правила, которые являются в основном теми же самыми, что и утверждения, которые мы обсуждали в предыдущей главе, и охватим любые критерии, которые точно не укладаваютса в нашу конструкцию таблицы, столбца и домена. Реализация не подразумевает существования бизнес-правил аа дайной стадии процесса проектирования. Все, что шхййк сделать — соврать бизнес-правила, связанные е даиными, для последующего раеемотреяия. При определении бианес-правил вы можете получить некоторое дублирование правил и доменов для атрибутов, но в данный момент его не является проблемой, -Важно задокументировать столько правил, сколько возможно, так как пропущенные бизнес-правила будут иметь для ааа большие последствия, нежели пропущенные атрибуты, отношения или даже таблицы. Новые таблицы и атрибуты будут часто найдены, когда вы будете реализовывать систему, обычно по необходаммти, но выяснение, что нужны новые бизнес-правила на последней стадии разработки, может разрушить весь проект, .вызывая дорогое переосмысление или опрометчивое "аалатывание дыр" в них. Разработка бизнес-правил — не очень трудный процесс, но это — потраченное время и довольно утомительно. В отличие от сущностей, атрибутов и отношений нет никакого прямого определенного алгоритме для выявления всех бизнес-правил. Однако моя общая практика, когда я рассматривал визнес-правила, дает вывод, что яужно читать документы строка за строкой, отыскивая предложения, видючаюпще слова, наподобие "как только ... происходит", "... надлежит ”... должен "... будет и т. д. Но документы не всегда включают каждое бизнес-правило. Вы могли бы просмотреть сотню или тысячу счетов, и ие увидеть ни одного случая, где клиент отдает в кредит деньги, но это не означает, что это никогда не бывает. Во впюгих случаях бизнес-правила могут быть добыты из даух мест: □ старый код; □ клиенты. Получение бизнес-правил с помощью любого из этих источников никогда не является приятным делом. Не каждый программист пишет удобочитаемый код. Если вам повезет, ваш деловой аналитик возьмет интервью у клиентов относительно этих правил. В нашем примере "фрагмента записей о встрече" есть несколько бизнес-правил, которые мы можем определить. Например, мы уже обсудили потребность в атрибуте, представляющем собой номер заказчика, но не могли определить домен да номера заказчика, поэтому возьмем следующее предложение: Оям AiiMjhfh/ шж&юЛ. и получим бизнес-правило, наподобие следующего: Номера заказчиков формируются на основе алгоритма работы существующей системы. (Проверить значения их текущих номеров.)
Другое предложение в нашем примере дает еще одно возможное бизнес-правило: С4яй№~^Лж^1л ш1аямашл вфняЛяию Для него мы можем получить следующее правило; Счета могут быть напечатаны только в определенный день недели. Однако действительно ли «о фактическое бизнес-правило? Маловероятно, что клиент хотел бы связать себя системой, в которой можно печатать счета только в некоторый день недели. Однако вы могли бы заявить, что это точно то, что клиент указал в первоначальной документации. Это, вероятно, будет одной из ситуапий. где английский язык (а русский тоже. Прим, перев.) недостаточно технически точен, чтобы определить важные детали в такой короткой форме. Вышеупомянутое извлечение на примера документации могло бы подразумевать: клиент ожидает, что будет столько документов, которые достаточно печатать еженедельно, или что документы удобно печатать еженедельно. Мм должны будем выяснить, что предполагалось в этом утверждении, когда созданный нами документ будет рассматриваться клиентом. Последнее бизнес-правило мы можем получить из следующей части документа: ... w nocmaAf- ляяяяся иймЛшт, Ыс amh ЛЛьЙиА кяввя МШ, Ют mjf Мфит (нш mkiih-a гфа/апф Мы имели с ней дело раньше, когда рассматривали атрибуты, но она заключает также очень важное бизнес-правило: Поставки не должны выполняться немедленно, если заказчики некредитоспособны. Конечно, мы должны определить: □ Что здесь означает термин "немедленно"? □ Как мы оцекиваем кредитоспособность клиента? □ Когда мы в конце концов можем отправить изделия заказчикам, как будут осуществляться платежи и как будут сделаны поставки? Эти проблемы также должны быть выяснены при дальнейших консультациях с клиентами. Данный раздел, посвященный бизнес-правилам, вряд ли полон. Легко привести примеры таблиц, атрибутов и отношений, но бизнес-правила имеют гораздо более неясную природу. Чтобы сделать наш пример таким, чтобы можно Выло получить большую часть
бизнес-правил, мы должны будем существенно увеличить количество информации в документации примера, что сделает идентификацию сущностей и атрибутов излишне сложной. Просто очень важно отметить каждое возможное место, где может выть получено то или иное бизнес-правило, которое определяет, как используются данные и которое не должно конкретно управлять доменами столбцов, ^ааяаыеяие бизнес-правил — потенциально протяженный процесс, который нельзя обойти или сократить в реальном проекте. Отсутствие или неполные бизнес-правила вызовут большие проблемы позже. Идентификация основных процессов Процесс обычно представляет совой логически связанную поеледовательноеть. шагав, предпринимаемых программой, которая использует выделенные нами данные, чтобы что-то с ними сделать. Как пример, рассмотрим процесс получения водительских прав (по крайней мере, здесь, в штате Твянввси): □ Заполнить разрешающие формы ученика. □ Получить разрешение на обучение. □ Практика. □ Ваполвить форму водительских прав. □ Пройти экзамен на вождение. □ Получить фотографию. □ Получить права, Каждый из этих шагов должен выть завершен до перехода к следующему. Процессы могут включать все или не нее шаги, перечисленные на логической стадии, и конечно будут иметь бианес-правала, управляющие ими. Процесс получения водительских прав в штате Теннесси требует, чтобы вам было пятнадцать лет для получения разрешения жа обученж», выло шестнадцать лет для получения водительских прав, чтобы вы сдали акзажен, практика должка быть с лицензированным водителем и т. д. Если вы являетесь деловым аналитиком, помогающим проектировать систему получения водительских прав, то были бы должны в какой-то момент задокументировать этот процесс, - 2 - Идентификация процессов очень важна для задачи маделвроваяия данных. Многие процедуры в СУБД трейуют раво-ты с данными, а процессы являются решающими в этих задачах, Каждый процесс будет обычно переводиться в один или большее количество запросов или хранимых процедур, которые могут потребовать большего количества даииых, чем мы определили. В нашем случае мы имеем несколько примеров таких процессов: :Имиви вин гфЛюя явдфкяннш иЛзжя ... Эта запись говорит вам, что есть средства создания и работы с заказами изделий, купленных клиентом» Внешне это не похоже на кардинальное открытие, но, вероятно, будут иметься записи в нескольких таблицах, чтобы облегчить надлежащее создание -заказа. Мы еще не определили таблицу заказа или заказываемых изделий, или даже цены изделии, хотя нам, несомненно, нужно вто вделать,
Ciesa-^s^tf шияакшся Мы сделали запись,о счетах, но этот процесс “печатания счета" иожет нотребввать дополнительные атрибуты для определения, что ечет-фамтура был напечатан или перепечатан, для кого он выл напечатан ж иожет ли он выть перепечатан, Управление докумеитами — очень важная часть многих процессов, которая может помочь в модернизации бумажной системы. Обратите внимание, что печать счета-фактуры может походить на довольно глупую процедуру — нажимай на кнопку на экране, и принтер выдаст бутгу, Вее, что мы должны - . сделать, -выбрать некоторые данные из таблицы, не так у;к и много? Однако когда мы - печатаем докумеет, нам, вероятно, придется делать запись факта, что документ был напечатан, кто напечатал его, и каково использованяе документа. Нам вы также могло потребоваться указать, что документы напечатаны -в течение процесса, который включает операции включения и суммирования продаваемых единиц в счете-фактуре. Здесь наиболее важный момент заключается в том, что мы не можем делать любые предположения. Другие основные процессы, которые были выявлены, следующие; □ Организация поставок — из поставки делаются немедленно, как только заказчик делает заказ, если ему доверяют □ Изменение счета-фактуры — из "Любые изменения счета-фактуры должны быть задокументированы,“ □ Поставки изделий — на "Все изделия отправляются во склада в центре города." □ Организация подтверждения заказа изделия — из ”... факс или электронную почту для организации подтверждений." □ Контакт с заказчиком — из “Желательно также иметь свободную область для контактов, т, к. они могут вести журнал контактов с заказчиками.” Каждый из этих процессов определяет единицу работы, которую нужно выполнить на стадии реализации проекта БД. Где мы сейчас? Должно выть совершенно очевидно, что ие все сущности, атрибуты, отношения, бизнес-правила и процессы даже для нашего простого примера были идентифицированы на данный момент. В этом разделе мы кратко рассмотрим- шаги, которые нужно выполнить для завершения задачи формирования набора рабочей документации. Каждый из следующих шагов может показаться довольно очевидным, но их можно легко пропустить или проскочить в реальной жизненной ситуации. Реальная проблема состоит в том, что если вы не выполняете весь процесс разработки, то можете пропустить огрошюе количество информации, часть которой может быть жизненно важной дай успешного проектирования. Часто, когда архитектор достигает этого конкретного момента, он/она будет пытаться перейти к реализации проекта. Однако имеются, по крайней мере, еще три шага, которые мы должны выполнить, прежде чем сможем начать следующую стадшо проектирования. □ Определение ввобходимых дополнительных данных. □ Рассмотрение проекта с клиентом. □ Повторение процесса, пока и мы не будем удовлетвореиы, н клиент не будет удовлетворен и не скажет, что можно остановиться на том, что мы разработали.
Обратите внимание, что эти процедуры должны выполняться в течение всего процесса проектирования, а ие только в той части, где определяются данные. Определение необходимых дополнительных данных Вплоть до агора момвитв мы старались ив расширять информацию, которая была получена на стадии не сбора. Целью выло получение основы нашей документации, так что мы могли довольствоваться грудами документов, которые первоначально соврали. Добавление сюда наших новых мыслей до выяснения того, что было в исходной документации, может запутать как клиента, так и нас самих. Однако на данном этапе нам нужно изменить стратегию н начинать добавлять атрибуты, которые мы считаем нужными. Обычно имеется довольно большой набор очевидных атрибутов и, в меиьшей степени, бизнес-правил, которые не были определены ни одним ив пользователей или на основе начального анализа. Для каждой и» идей н элементов, выделенных к настоящему моменту; нам нужно тщательно рассмотреть и определить дополнительные атрибуты, которые, как мы думаем, будут необходимы. Например, воэьмвм сущность "Контакт", которую мы определили ранее: Сущность Описание Контакт Люда, выступающие в качестве заказчик», и с которыми следует установить контакт Атрибуты Номер факса Номер телефона, чтобы посылать факсимильные соовщеяиж заказчику - Любой подходящий номер телефона Номер голосового телефона . ' . - . Номер телефона для голосовой связи Любой подходящий номер телефона Адрес Почтовый адрес контактного лица Любой подходящий адрес Адрес электронной почты Адрес электронной почты контактного лица Любой подходащий адрес электронной почты Отношения Имеют бухгалтерский журнал Для определения, когда они контактировали по адресу заказчика Мы вероятно хотели fci отмети», что контактное лицо будет иуждаться в следующих дополнительных атрибутах: □ Имя — полное имя заказчика, вероятно, наиболее важный из всех атрибутов. □ Имя супруга (супруги) — имя мужа или жены заказчика. Этот вид информации бесценен при создании контактов, если вы хотите персонифицировать ваше. сообщение или спросить отвосительно семейства заказчика.
□ Дети. □ Дата рождения — если день рождения человека известен, можно послать к этой дате открытку. Имеется, конечно, большее количество атрибутов, которые мы могли бы добавить к сущности "Контакт", во этот набор должен сделать сущность достаточно ясной. Могут также иметься дополнительные таблицы, бизнес-правила в т. д.( чтобы рекомендовать клиенту иа этой отв дни проектирования задокументировать их я добавить к вашим спискам. Одна из главных вещей — удостовериться, что ваши новые элементы данных отличаются от тек, которые уже согласованы с клиентом на предыдущих консультациях, и от предварительной документации. Повторное рассмотрение с клиентом Как только вы закончили собирать згу первую подборку документов, пришло время встречаться с идаентом, Важна показать ему все, что у вас есть, и ничего не пропустить. Совершенно необходимо сделать -так, чтобы клиент рассмотрел каждую часть этого документа и понял то решение, которое вы начинаете разрабатывать для него. Конечно, клиент может иметь очень маленький или никакой опыт в вопросах проектирования БД и может требовать помощи и объяснений от вас, чтобы лучше понять ваш проект. Стоило бы даже сделать два документа, один для клиента в терминах обывателя и другой внутренний, более детальный для использования вашей командой проектировщиков. Заслуживает внимания разработка иеиоторой формы документа отказа от претензий, который клиент подпишет до того, как вы .продолжите свою работу. Это могло бы быть хорошим юридически обязательным документом. Повторяйте ваши действия, пока не получите одобрение заказчика Конечно, маловероятно, что клиент немедленно согласится со веем, что иы ему говорите, даже если вы -— величайший архитектор данных в мире, Обычно требуется нееколько попыток, чтобы получить его согласие, и каждое повторение должна продвигать вас и клиента ближе к цели. Поскольку вы выполняете все больше повторений проекта, становится все более и более важным удостовериться, что на основании имеющейся у вас информащти клиент каждый раз отказывается от своих претензий; вы сможете указать на эти документы, когда клиент изменит свое мнение позже. Плоха, когда вы не выполняете соответствующей работы по управлению повторным рассмотрением и процессом документирования. Я занимался консультированием проектов, когда проект был хороша разработан и согласован, на документация а там, что выло согласовано, выла сделана не слишком хороша (уйма подтверждений, на высоком уровне, чтобы сэкономить деньги! ). Когда прошло время, и много тысяч долларов было потрачено, клиент пересмотрел документ соглашения, и стало очевидно, что мы вообще о многом не договорились, Само собой разумеется, что работа была выполнена не слишком хорошо.
Учебный пример книги Перейдем теперь к нашему реальному примеру проектирования ВД. Вернувшись к учебному примеру, начатому в главе 2, мы должны заиятьея разработкой сущностей на основе принципов, уже представленных в этой главе. Интервью клиента Всегда лучше начать е записей интервью е клиентом. Сначала мы просматриваем их и отмечаем все пункты, которые собираемся представить сущностями. В нашем примере мы будем помечать полужирным шрифтом эти пункты. кВяфкв- г Gww- Смияшч, few, 24 ноября 2000, -Ю шЛ jw^e, кжфифегщ-бая ЯСязашгюи; Лум ОэЫст, {фхншЬяор Ланта; Сан Смит-, мвнЛулр фриолафАяо ylem; 1Л kCh/иж, Люминол. №>.; МлтЛу&ша гяайшшшта! ЛЛуменкм рошавр бвн-кл, патЛи lek, патАоА лш/Ля Ля, а ишЬЛ -хгЛафаннъЛ фврлнп яиуе&ио Лшл. .S настоящее iaui иаяхг^аяся бумлнын регмиф iekot (che-cfc reyiater) а ооЛЛ мшАшА ймрфвшия фшшф. ‘‘РмашфЛяы ЛАшуноаи, мштМшся пАша/лешшх маяершлой, на ж мши ни аЛшо, Лепрый At оАаЛм&а ta, too xdta мСлСдл И у гляъшижт/а тфЛЛвусешшх ддити аишквм мной оаАнмюяЛ. Лущена имя» инлэшЛоваяшЛа» йоЛиЛноаки (Лй я яАсмопф). крАофЛмнж кфЛ ияяраяет. 'Прогул 'Hypfaw имшь aoGUMmo иифоумАуию шноашвпно скша (Ac-omtni) 45 илатшше фиа мяанс clxmi а^Адшая Лин fui t мшщ. ^^ашь^якя шккш (xtaiemer.f'j аЯ Оля падения Лмжа иат^яаг т ^аякеЛ мф полный дм. Хтяешл ш нам^ат бвлаж по day е^внЛтш, магт^я Лшли яЯ ЛЬгяфсувт витке йЛ/гяя сгапЛ, но не ЛСлл-ги:, Луни, оА&птап/а, и я. Э. Оля этой уу» аоыяая смсшыа. C>«'f/</ улАЛшося, в^шАш лАшм аупшфм. ОйнЛ» кшафмн&и гшна нвя. ЛяЛ Сам ^кяЛнл, был ЛАан Йюфвс Л шаеЛмАшин уомЛЛ. Stw бы хорош ммя№ информауша а я^каяем юлквфл бхя Алошшеа^ вшисм/мчмх нами tdcot. 'ТПЛс^л аанкиошыю кшяогфокуии 1екой (океек) Сэм оШм, кяо эаю бым би хороао, w ш явяявжя шоАм&шм f ЛнныА МОМОШ ^&№ЯЯ. и
Мы нашли следующий исходный ряд еущяаствй, в которыми буде.м иметь дало; Сущность Описание Cheek (чек) Бумажный документ» который используется, чтобы передать фонды кому-нибудь Account (счет) Банковские отношения, установленные. чтобы обеспечить финансовые .сделки Check register (регистр чеков) Места, где зарегиетриразано иеиельзоваяие счета Bank (банк) Организация, которая имеет текущий счет, вместо которого выписываются чеки Statement (отчет) Документ (бумажный или электровиый), который поступает от банка раа в календарный месяц и который сообщает нам соображения банка о том, что мы потратшш Payee (получатель платежа) Личность или компания, которой мы посылаем чеки
Следующим шагом мы просматриваем документ и ищем атрибуты (которые полужирным подчеркнутым шрифтом) и отношения, которые мы выделяем с СжмСмаягом, бухмлафкоо дело, 2Л нояфя Z000, 40 toot ^фаг Ьнффенц-Зая ЯЪ&шишвж J^pte 'DattAou, фашвдаяф башлык Сэм Сммя, ммЛфф б^азвяфАта tflana, Xhpe ©жь«с Лмииетг 'ТЦ&о- ЙяфеА КшлпЗуыш бяюжнтшше дфрленяп*; petttanp tanka, пшшЫ, iefc, пшпЛА яма/АЛ dan, а гыА/св змкяфоюшЛ формат лнуЛна Аша. £ ноашэуа фмя шжеЗушя бумажнмА $&r) рмныгф iAoS. а nuhja юАнА АурфеншА (инет) ЯюияфЛии бАшз/амвт шзжАвампя пА.явАнншн'о мшяфшА, ю не павам tat АшекАлфсА tv КкпхкАп все. itm хвАя КкЛК. Ку божшноЛ п^туеюшх m/xd а н Ком маша асЛнжаявА, tHipicHo HHtim мнвгоптАо/авилееКлв (Лд я ^вэйя^у. ЛафЛюкние гфЗ шлифашт. Кфауеа; Цроа имят гпсяЛшм шл^мтрлю аяжсшшю cleiua (account) £ нмяияяее фмя (аяанб Сяпа проЫивкя один fat S Mtatf. Маап^аюя банка 1)хя гл^тмя name,а шлахъЗ^аяся по мф тлнии. дет. ’Хшешт f№t шщ^аяе шаж ш Ляу еЛвяЛемп» (Ылм^ tetMyl мвюл^я fC М^фашк Aria] Мшаф^ея яохАо Senate аяяой, на не яфе^ния, AapM, "ААвшаЛ, tt я. д. Охя этою х-с тмшя ашкма, £фйВ1 фбнЗо/нхкя Kfcetahto шайным афНш1фам. ОЛнако конфа.пша _гшш нш. ’Когда Сэм a^ttmiwc, tea Задан Ъмт бы хфэш имвт ой (йюеэкЛ, hr!i&:Cl0:kta нами ЧЯЛ, KlahK опяша^шма каттбиЛауин (св^огШеш) lekot Сзм ФаМи, 1вю ж Алт Ал хфша, но не-я/шшся а^^адимот, f, датшА момент, фшени. I I В настоящее иремя Фактически нет полного набора информации, который нужно подобрать из этих заметок. Вй можете заметить, что интервью е клиентом раскрывает очень небольшую информацию; в этом случае мы можем захотеть понять, почему мы ве получили информацию, которую искали. Говорили ив с теми людьми в организации, или задавали неправильные вопросы? Неприятно отвечать на вопрос "Почему я потерпел неудачу? ” — но это сделает вас более хорошим архитектором данных в будущем.
Документы Наша документация учебиог© примера справедливо хромая (чтобы пример был коротким и приятным), ио, к счастью, мы имеем несколько документов, которые прольют немного света на наше решение. Мы сейчас рассмотрим эти документы и отметим атрибуты (и возможно, сущности), которые там найдем» Первый документ, который мы рассмотрим — типовой чек. Этот вид документа — мечта проектировщика. Так как мы уже определили сущность Check (чек), каждое из полей в этом документе, вероятно, будет атрибутом. Здесь CheckNwnber — номер чека; Payee- — получатель платежа; Date Written — дата заполнения; Мелю — служебная запись; Ажите — сумма; Signature — подпись. В нашем чеке можно отметить два момента: □ Термин Amount (сумма) определен на чеке дважды, обеспечивая меру избыточности и защиты от мошенничества, которые несущественны на этом уровне проектирования. Q Payee (получатель платежа) уже был определен как отношение тина "имеет", который, в сущности, является атрибутом таблицы, так что мы не должны его снова определять как атрибут. Следующее, что мы рассмотрим, — типовой регистр чеков. Это — просто запись каждого чека, как только он будет введен. Здесь Number — номер; Date — дата; Description — описание; Category — категория; Amount — сумма; Balance — баланс; Account — счет; Running Total — текущий итог.
Заметим, что номера, даты, описания (палучате-ли платежей), категории ж суммы уже получены непосредственно из чеков. но баланс- (Balance) — новый параметр. Атрибут Balance (баланс) не соответствует в полной мере сущности Check (чек), но имеет смысл для сущности Account (счет). Следовательно, мы добавим атрибут Balance сущности Затем мы должны рассмотреть отчет банка, Отметим все атрибуты так же. как мы делали в чеке, Отчет банка обеспечит атрибуты для нескольких сущностей, Account Stataaeift AccuserStiftsKt ctrct?- ИИИИИ* : -........ Account. AcCOJP' NuihLk-Q usee Cheek ПШ 12231 1Ш/00 Щ8Л» X4S393 H/2SW 12ЛЖ0 122H ШП 13ЛМ6 Cbeekr И • u t В s I -i .f e TsW brta» Tstal Credits Lstt 11/5ХЮ 12/5MS 12217’ 122SJ' 'OwSa’paWB тмчвкря Deposit BrectWIthdeanal Statement. flateicina.ltems the Btaamam consins copies of aBftefiem» the-egistet should havci Statelier-. ?ratOLiS Balance Depart 12213 Cheek Check 12211 PrMousfla&raOaM S:a-emei-t Data 1220S Dfrtet WybCfal^al Здесь Bank — банк; Bank Name — название банка; Accoudt >— счет; Account Statement — отчет no счету; Account Number — номер счета; Previous 'Balance — npeflw^tifeft ' баланс; Current Balance — текущий баланс; Total Debits — общий Небет; Total Credits — общий кредит; Last Balanced — дата последнего баланса; Stateent Date — дата отчета; Check — чек; Deposit — депозит; Direct Withdrawal — прямое изъятие; - Balancing Items — элементы баланса; Previous Balance Date — дата предыдущее баланса.
Отчет Даяка дал вам несколько атрибутов для сущностей -— Bank (баяк), Account (счет) и Statement (отчет). Здесь мы определяем две новых сущности: □ Deposit (депозит) — нспатзувгся для добавления денег к счету; □ Direct Withdrawal (прямое изъятие) — деньги, изъятые со счета без заполнения бумажного чека. Снова мы имеем дату чека, номер чека, сумму чека и текущий итог (все вычеркнуто ио причинам секретности и безопасности, хотя всегда хорошей идеей является изображение вида некоторых документов, содержащих конфиденциальную информацию, е помощью отображения диапазона цифр и их формата — например, номера чеков дополнить ведущими нулями). Мы также обращаем внимание, что сущности Deposit (депозит) и Direct Withdrawal (прямое изъятие) имеют, очевидно, одаи и те же атрибуты^м они — все в той же самой таблице с текущим итогом. Так что сущности Deposit и Direct Withdrawal будут иметь атрибуты Data (дата), Number (номер) и Amount (сумма). Текущий итог снова является общим для счета. Заключительная часть документации, которую мы должны рассмотреть — электронный формат регистра банка. Электронная версия более редкая, чем бумажная версия. В электронной версии может выть и вольше информации, но его — нее, что нам в настоящее время дают. Она также может-выть единственной, что нам дают, потому что это действительно все, что нам нужно для балансирования счета. Column Data type Required Trans acti*o Date Date Only i':: Transaction Num b ;r . 5:r:ng(20) Yet Description StringCTO) bn Де» Amount Msm? - Transaction, Descr.pti.xi ; ttem Account Здесь Column — столбец; Data type — тип данных; Required — требуется; Transaction — сделка; Transaction Date — дата сделки; Transaction Dumber — номер сделки; Description — описание; Item Amount — сумма; Date Only — только дата; String(NJ — строка из N символов; Money — денежный тип. В то время как здесь нет никаких новых атрибутов для уже имеющихся сущностей, имеется упоминание о новой сущности — Transaction (сделка), Интересно, что банк использует ату сущность с соответствующими атрибутами, чтобы сообщить о значениях некоторых других сущностей. Все наводит меня на мысль, что, вероятно, имеется едаиый объект, который одновременно представляет и чеки, и депозиты, и прямые изъятия. Это указывает на то, что мы будем иметь сущность Transaction с отношениями "является” к сущностям Depoat, Direct Withdrawal и Check.
Проработав все наши документы, мы должны закончить наш документ сущностей. Здесь мы отметим все дополнительные атрибуты, которые добавим в самый левый столбец. Перечень объектов банковского счета Вот этот набросок документа, который мы формируем на основе нашего первоначального анализа: Сущность Описание I Домен Transaction (сделка) Логическая операция, которая добавляет или изымает деньги яа нашего счета Атрибуты Transaction Date Дата, когда деньги добавлены Подходящие (дата сделки) или удалены со счета даты Transaction Number (номер сделки) Уникальный номер, используемый для идептификацип сделки . - Строка Description (описание) Строка описания сделки Строка Item Amount Количество денег, добавленных Вещественное (сумма) или удаленных со счета таем© е даумя десятичными цифрами Check (чек) Бумажный документ, который используется, чтобы передать фонды кому-нибудь Атрибуты Cheek Usage Type (таит использования чека) Определяет категорию использования чека Неизвестный Check Туре (тип чека) Определяет категорию чека Неизвестный Check Number (номер чека) Номер для однозначного определения нежа Целое число Date Written (дата заполнения) Дата, когда чек был написа н Подходящие даты, возможно, не будущие даты Amount (сумма) Количество денег, снятых со счета и переданных получателю платежа Вещественное положительное число с двумя десятичными цифрами Продолжение таблицы. на следующей странице эз
Сушн»еть Описание Домен Мето (служебная вались) Краткое описание? для чего используется чек Текст Signature (подпись) Подпись лица, выписавшего чек — мы, возможно, просто храним текстовую версию имени подписавшего Текст Отношения Имеет получателя платежа Используется для идентификации получателя платежа, которому послан чек Является сделкой Определяет, что чек является подклассом сделок Payee (получатель платежа) Личность или компания, которой мы посылаем чеки Атрибуты Новый Name (имя) И.-,:я любого, кому заплачены деньги Строка Новый Address (адрес) Адрес получателя платежа Любой подходящий адрес Новый Phone Number (иомер телефона) Номер телефона получателя платежа Любой подходящий номер телефона Deposit (депозит) Используется для добавления денег на счет Атрибуты Date Written (дата написания) Дата написания чека Подходящие даты, возможно, не будущие даты Deposit N umber (номер депозита) Номер для однозначного определения депозита Целое число Amount (сумма) Количество денег, добавляемых к счету Вещественное положите л ьн ОС- число с двумя десятичными цифрами Description (описание) Описание из электронного источника Строка Отношения Является сделкой Определяет, что депозит является подклассом сделок
Сущность Описание Домен Direct Withdrawal (прямое изъятие) Используется для изъятия денег со счета без какого-либо бумажного документа Атрибуты Date Withdrawn (дата изъятия) Дата, когда был иаписаи чек Подходящие даты, возможно, не будущие даты Number (номер) . Номер для однозначного определения чека Целое число Amount (сумма) Количество денег, святых со счета Вещественное положительное число с двумя десятичными цифрами Description (описание) Описание из электронного источника Строка Отношения Является сделкой Определяет, что прямое изъятие является подклассам сделок Account (счет) Банковские отношения, установленные для организации финансовых сделок — мы, вероятно, будем иметь дело со многими банками Атрибуты Account Number Число,: однозначно Неизвестный, (номер счета) определяющее счет устанавливается банком, возможно, строка Balance Date (дата баланса) Дата и время, когда произведен баланс счета Подходящие даты Running Total Текущее количество денег на Вещественное (текущий итог) счете положительное ч и с л о л двумя десятичными числами Отношения Проведение баланса, Определяет, получаются ли данные из баланса счета используя отчет Использует сделки Для счета используются сделки, чтобы добавлять или изымать деньги Продолжение таблицы на следующей странице
Сущность Описание Домен Cbeek Register (регистр чеков) Место, где фиксируется использование счета Атрибуты Register Туре Описывает тип регистра, который мы используем для хранения записей счетов "Документ" Bank (банк) Организация, которая использует чековые счета и которая выписывает чеки — мы, вероятно, будем иметь дело со многими Санками Атрибуты Bank Name (название ванна) Название банка, с которым мы пмоеУт дело Строка Отношения Рассылает отчеты Банк рассылает отчеты, чтобы мы могли сбалансировать счет Имеет счета Идентифицирует счета банка Statement (отчет) Документ (бумажный или электронный), который поступает из банка раз а календарный месяц и сообщает нам все, что думает банк о наших тратах Атрибуты Statement Туре (тип отчета) Определяет тип отчета, полученного из банка "Интернет-данные", "Документ" Previous Balance (предыдущий баланс) Определяет, какой предполагался баланс счета после последнего баланса отчета Вещественное число с двумя десятичными цифрами Previous Balance Date (дата предыдущего баланса) Определяет дату последнего баланса счета Дата
Сущность Описание Домен Current Balance (текущий баланс) Определяет Велаке счета после того, как все элемеиты отчета были сформ ирован ы Вещественное число е двумя десятичными цифрами Statement Date (дата отчета) Определяет дату, когда сформирован отчет — это, вероятно; тот день, когда отчет создан банком Total Credits (общий кредит) Сумма всех элементов, которые добавляют деньги на счет в период, охватываемый отчетом Вещественное положительное число е двумя десятичными цифрами Total Debits (общий дебет) Сумма всех элементов, которые изымают деньги со счета в период, охватываемый отчетом Вещественное отрицательное число с двумя десятичными цифрами Balancing Items (элементы баланса) Все операции, которые выполнил баяв, и о которых он теперь оповещает нас Массив сделок (чеков, депозитов, прямых изъятий) Вы можете, вероятно, заметить, что мы достигаем такого состояния, когда даже для такого маленького примера этот формат документации становится неуправляемым. В следующей главе мы обсудим моделирование данных, которое обеспечивает метод сбора и представления этих данных, являющийся намного более легким для работы. Это не означает, что данный вид описания не имеет никакого смысла. По крайней мере, m должны сформировать этот документ, чтобы представить клиентам. По этой причине мы выполнили это упражнение, а не перешли непосредственяо к моделированию данных. Документ, который мы сформировали, дает хорошее описание того, что мы хотим реализовать либо техническим, либо нетехническим способом.
Бизнес-правила и процессы Для рассмотрения бизнес-правил и клиентом. Процессы пометаны ТМИ is мы вернемся к записям нашего имтервыо с правила] обведены рамной Яафем г- Сэмом Смшам, Арм/Шрскм Aw, 2-4 ноябри Z000, 10 leaf у/фа, Ъмшб 'llawstyytMbig Аилчтаошая деЬришж; рямаА Аяка, яишЛа lek, хш^А с1яп, а зяакксе здАвАоткА фармлт мшЛм с1ша. яашшвуа ^вмя иат^шт А.1яг,тр itkof, л яакфи йриоЬши фммф "РасЕммфЛлн ААифяоа» шшАо&шш аА^Лчшшх мАшфиляов, т не наши ни Лшо, kaae^ttA оы oAcaehtiu ва, км хЛя- ЗакваАк J-4 АяляишпА гфЛбАмяых гшаек ашш!ам ммш оаявннешии. Зрркяо им»'"» мнаопачвЗвбяяеяАи ^ю^юет (tkA я иросмовф). Лагребатж i^e3 Нум и» имяп поаебнт ямрф, £ иааюкум бремя Азине- ййт?*] t моему. ккжяЗуякя аи1ш иЗ (Ыа. $)яя ип^шея-Аяш» эитяАувкя т ;фак>мй мф rnxfutp. дшъ-^^зша ба шяршьАяаж по Ляпу ^инЛш№ (baiarxA иашАуп I Ашш м3 (internet data) ИааЙй/ик шякя Adtmte Лшб, ж ж dt^t&tens, акуим, АеЛяляшЛа, и я. i (Dxn эйп» уре- tmesnea аиетма. ЛгА Gm Акянкеа, Аа Ашн мири @6 джаоАмваши фмлр:р,ф Ъыло Ал мфож нмввя зтрфиауек е яв^аят Ая Адюишаайа Зтмик’Лкммх шмя iAA ЯШф оатишшш кашп^Звуки (са^ог^пу) йЫ Озм скмЗл% 1яо это Адяо бы хороню, но не яЬаякя нйовходимым t дянныЛ момент йрАмени,
Эта документация дает нам следующее , -> Бизнес-правило Описание 1 Новое Новое Должны быть многопользовательские возможности Составлять баланс по счету нужно еженедельно Будет ревизоваться ежегодно Пользователь должен полупить предупреждение» если попытается забрать слишком много денег Не следует вводить сделки, которые произойдут в будущем Возможно, что одновременно потребуется вводить сделки более чем одаому человеку Нам нужно иметь возможность балансировать счет более часто, чем обычно один раз в месяц; возможно, чтобы уменьшить время между моментами баланса, нужно будет находить пропущенные сделки более часто Некая внешняя фирма будет проверять наши числа ежегодно, чтобы удостовериться, что вое находится в хорошей форме; вероятно, мы сформируем некоторую документацию, чтобы облегчить этот процесс Пока это только идея, что пользователь может пожелать иметь помощь, чтобы избежать превышения счета Можно избежать пост-датироваиия чека Процесс Описаете Новый Составление баланса счета Ревизия счета Ввод сделки Урегулировать все сделка с точки зрения банка, чтобы мы должны были удостовериться, что все из них зарегистрированы Процессу чтобы улостове-риться, что все именно так, как зарегистрировано Ввод нового депозита, чека шш прямого изъятия Имеется, вероятна, большее количества бизиес-правил я процессов, которые йогу» выть предложены, но то, что мы имеем — хорошее начала. К этому моменту вы сформировали проект документа для одобрения клиентом.
Метод водопада Заключительный момент, ив который мы должны обратить внимание, состоит в том, что вам, возможно, потребуется выполнить весь этот процесс много раз, в зависимости от того, как хорошо вы выполнили процесс, описанный в этой главе. Те, кто имеет опыт руководства проектом, могут заметить, что процесс, отраженный в этой кише — точный метод водопада, который описывает процесс проектирования программного обеспечения подобно следующей схеме: Она описывает некоторый путь, который мы выполняем здесь в процессе проектирования ВД. Однако на практике этот процесс был бы более сложен, чем изображенный на предыдущем рисунке, Фактически он представлял бы цикл, а не линейно следующие друг- за другом блоки. Как результат стадии тестированы, может потребоваться включить в проект новые определенные описания; ото подразумевает, что весь процесс должен начаться заново. Каждый новый запуск цикла называется итерацией. Любой программный продукт или приложение выполняет много итераций, прежде чем завершит работу, Проблема добавления циклической информации состоит в том, что мы можем вернуться от кодирования назад к проектированию или от проектирования назад к описанию. Если все идет точно согласно плану, то-вышеупомянутая диаграмма иллюстрирует полный процесс проектирования, Разумеется, решение в конечном счёте должно быть получено, и ключ к этому — попытаться ограничить число итераций всякий раз, когда ото возможно. Несколько больших изменений намного лучше, чем много небольших изменений.
Резюме В этой главе мы рассмотрели процесс нахождения сущностей, которые, н конечном счете, составят основу решения вашей ВД. Мы пропахали всю документацию, которая была получена на стадии сбора информации. При этом старались не добавлять наши собственные идеи в решение, пока не обработали вею исходную документацию, Следует заметить, что это не такая уж маленькая задача; в нашем исходном примере мы имели для работы только два абзаца, а завершили почти полутора страницами документации, Мы также провели интервью с клиентом и получили его согласие. Следует.отметить, что запись всей этой информации в простой текстовый документ — не идеал. Мы выбрали этот способ размещения просто для целей этой кииги, но обычно мы используем специализированные средства для формирования такого вида документации. Процесс моделирования данных, к которому мы перейдем в следующей главе, сформирует скелет документации с незаполненными местами, в которые вы будете вводить вашу информацию. До сих пор мы занимались только той частью процесса проектирования, которая связана с описанием. И этого действительно нельзя избежать. Любой процесс разработки, который требует хранения данных, должен полностью определить эти данные до перехода на стадию проектирования. Стадия реализации будет рассмотрена во второй половине этой книги, когда мы перейдем к физическому проектированию. Когда процесс, рассмотренный в этой главе, будет .завершен, мы будем иметь почти все, что должны взять от клиента, прежде чем создадвать точную спецификацию данных.
Моделирование данных Введение Термин "моделирование данных" часто используется в очень широком смысле для описания всего процесса проектирования ДД. Такая формулировка не так уж и плоха, но> здесь мы будем понимать под данным термином не это. В этой главе мы будем вспоадзовать его для обозначения процесса разработки графических представлений структур данных. Документ, который жа 'лгу.ялп разрабатывать в предыдущей глете, стал веуправляеад>ш за короткое время, и идентификация отношений между различными сущностями стала очевь трудной. Следовательно, мы нуждаемся в некотором способе представить информацию, которую представили как таблицы в документе, в более понятном формат». Чтобы сдавать это, мы изображаем графические модели (картинки), чтобы преобразовать документ в форму картинки. Имеется много типов моделей или диаграмм; модели процесса, диаграммы потока данных, модели данных, диаграммы иоеледомтельностей; список мод бы продолжаться в течение весьма долгого времени. Мы, однако, сосредоточимся на двух специфических моделях: □ Модель действий (Use Свае) — модель, которая входит в более общее описание, известное как UML (см, ниже), используемое дав моделирования нужд идрйований пользователей. Модель действий предназначена для задания всей известной о проекте информации, чтобы она была удобочитаемой как для пользователей, тан и для проектировщиков, □ Модели данных (Dat* Models) — модели, которые яовоетью связаны е представлением структур данных в реляционной ВД. Прежде чем мы посмотрим на вышеупомянутые модели болае внимательно, обсудим кратко методологии моделирования, которые Вудам использовать в каждом случае. =-
Методологии моделирования Модель действий фактически является частью более общего описания, называемогоUML (Unified Modeling Language — унифицированный язык моделирования), UML-описаяие не имеет средств моделирования данных для реляционных ВД (это — объектно-ориентированная технология), так что мы должны будем использовать другую методологию моделирования данных. bta остановим наш выбор методологии моделирования данных, взяв ту, которая является самой легкой для чтения, а также позволяющей отображать и размещать все, что нам потребуется. В этой книге мы сосредоточимся на одной из наиболее популярных форматов моделирования для реляционных ВД — IDEF1X (Integration Definition For Information Modeling — интегральное определение для етформаджонного моделирования), и затем кратко коснемся другого формата — IE (Information Engineering — информационная разработка). - В то время как методология моделирования данных может выбираться исходя из личных предпочтений, экономика, стандарты или возможности компании обычно влияют на выбор средств. В этой главе мы попытаемся непредвзято посмотреть на некоторые особенности средств, которые требуются или желательны, если нужно расширить основы графического маделировататя. Вся информаиия, найденная в документе, лотизрый мы создавали в предыдущей главе, должна'выть представлена и разйещева в модели данных, и мы должны иметь возможность получать эти данные, чтобы поделиться с клиентом и программистами, UML UML — де-факто стандартная методология для определения и документирования компонент объектно-ориентированных программных систем, возданная в значительной степени благодаря работе трех людей: Гради Вуча, Ивара Якобсона и Джима Рамбо. Каждый из этих людей имел свои собственные (или частично советненные) методологии, но они выли объединены вместе, поскольку все эти люди начали работать вместе тал продуктом Rational Rose на фирме Rational Software. Одаако это не закрытая методология, и она сегодня используется и стандартизирована в программных средствах, предлагаемых многими большими корпорациями. В настоящее время полную документацию версии 1.8 можно найти в http://www,raflonal,com/uml. Корпорации, которые внесены в список использующих UML-описание: Rational Software, Microsoft, Hewlett-Packard, Oracle, Sterling Software, MCI Systemhouse, Unisys, ICON Computing, IntelliCorp, i-Lagix, IBM, Object Time, Platinum Technology (CA), Ptech, Tash an, Reich Technologies,, So f team, По-видимому, важно упомянуть здесь, что UML не только методология моделирования, но и законченный метод объектно-ориеитированнсч-о проектирования. В to время как методология моделирования — просто метод графического отображения кошюавнт компьютерной системы, метод проектирования влечет за собой проектирование системы “от колыбели до могилы". UML предъявляет невысокие требования, когда мы хотим модадаровать процессы типа тех, которые обнаружили при рассмотрении прошлой главы. Другая цель состоит в том, чтобы дать прямое объяснение того, почему при моделировании данных следует использовать UML, хотя мы не будем использовать его непосредственно для построения или моделирования наших реляционных данных, так как (об этом было сказано ранее) он предназначен не для реляционных технологии ВД,
UML составлен из весьма небольшого числа различных моделей, каждая из которых используется, чтобы представить компоненты системы, но мы используем только одну из них. Для чистого архитектора данных, имеющего дело только о ноетроеиием реляционной ВД, большинство моделей, вероятно, не так интересны, аа исключением диаграмм действия (use case diagram). Позже мы дедам краткий обзор методики моделей действий на таком уровне, которйй позволит использовать ее в нашей задаче проектирования СУБД. IDEF1X Рассмотреть вое существующие методологии моделирования было Вы почти: невозможно, Все они служат одной и той же цели и все имеют почти одни и те же объекты, отображаемые слегка различающимися способами. В этой главе мы остановимся на одной из наиболее популярной из нвх — IDEF1X, которая основана на сообщении FIPS (Federal Information Processing Standard — федеральный стандарт по обработке информации США) №184 от 21 сентября 1993 г, .Чтобы быть справедливыми, имеется несколько методологий моделирования, которые являются в значительной степени эквивалентными для миделиромтя данных, типа IE (см. выше) и Chen ERD (диаграмма Чена отношений между сущностями), и если вы использовали адиу из этих методологий, то вы, вероятно, привыкли к ней и не пожелаете изменить технологию на основе этой главы. Однако все примеры я книге будут в IDEFlX-формате. IDEF1X была первоначально разработана в Военно-воздушных силах США в 1985 г., чтобы обеспечить следующие требования: □ поддержка разработки моделей данных; □ быть языком, который одновременно и легок в изучении, и является мощным; □ быть доступным;. - О быть хорошо протестированным ж проверенным; □ быть подходящим для автоматизации. Для получения полной копии IDEFlX-описания обращайтесь к Mtp://'wvAv. qd .d/webpages/id ef/idef 1/hJef 1 x. html. IDEF1X очень хорошо работает в случае предъявления вышеупомянутых требований и используется во многих популярных средствах проектирования, типа СА ERwin, Embarcedero ERStudio и Microsoft Vieio Enterprise Edition. Она также поддерживается другими распространенными средствами, которые можно найти в Интернете, Теперь, когда мы обсудили методологии, которые (удам использовать, посмотрим, как их применять к нашим моделям. Модели действий Как описано в Руководстве по UML, версия 1.1, издаивая 1 сентября 1997 г. (полный документ находится в http://www.rational.com/umD, модели действий (Use Саве) представляют внутри системы функциональные возможности этой системы или класса так, как это видят внептние пользователи.
Модель действий описывает:. □ компоненты системы, гаюдеянроваивыв как действия (use cases): П пользователей системы, смоделированных как исполнители (actors): □ отношения между исполнителями и действиями. Очень упрощенно модели действий представляют абстракцию диалогов между исполнителями и системой. Они описывают потенциальные взаимодействия, не вдаваясь в детали. Этот тип информации существенен для архитектора ВД» так как нам всегда нужно идентифицировать пользователей. которые будут обращаться к ВД, и то, что оня хотят делать. Имеется несколько очень важных причин для этого: □ безопасность; □ обеспечение данных, необходимых для поддержания существующего процесса; □ создание хранимых процедур и триггеров -для поддержания процесса. Как мы увидим в следующем разделе, модали данных вообще и® имеют отношения к процессам или пользователям. Так как модели действий являются частью UM.L, оии часто используются не только строго <въектво-ор»нтированиыми проектировщиками, во также и архитекторами систем всех типов, чтобы получить чертеж действий, которые должны дать желаемый результат. Различным категориям проектировщиков/архитекторов при использовании различных технологи! моделирования понадобится использовать различные части диаграмм действий (как и нам с нашими моделями данных), Нет никакого набора приемов описания действий, но имеется определенный тля модели. Диаграммы действий очень просты, и имеются только два символа, используемые в них. Первый символ — исполнитель (actor), который является фигуркой человечка: Исполнитель представляет пользователя, систему или часть аппаратных средств, которые ыпшшяют действия в системе. Второй символ -— действие (use case) — изображается обычным эллипсом с названием внутри: L Действие
Чтобы смоделировать факт, что мшолнитвль использует действие, ми два символа связываются одинарной линией, именуемой отношением коммуникаций (com mnni rates relationship): Элементы действий могут быть также связаны друг е другом двумя способами: □ с помощью другого действия для завершения цели — отношение "использует'’ ("uses"): □ расширяя другое действие, чтобы уточнить его поведение —отношение ’расширяет" (“extends"). Они моделируются очевидным образом: Пусть, например, мы хотим смоделировать процесс создания книги. Мы имеем, по крайней мере, слеадющих исполнителей: издатель, принтер, редактор и (надо надеяться) автор. Тогда нам понадобятся следующие элементы дейивий: предложение книги, прием книги, написание квит, редактирование книги, печатание книги и (мое любимое) плата за книгу. Это, вероятно, не включает всех исполнителей и действий, нужных для модвяироватя процесса публикации книги, но вполне достаточно для нашего примера. Отсюда мы получаем следующую модель:
Один момент может показаться странным в этой диаграмме — прием книги находится во предложения книги. Однако в диаграмме действий нет никакого упорядочения. Упорядочение будет позже при рассмотрении процесса. Фактически, в очень большой диаграмме действий могут быть сотни элементов действий, связанных со многими исполнителями. Каждый элемент действия будет овычио иметь информацию, объясняющую его назначение. Следующая таблица показывает возможное описание для одного из наших действий: Название Написание книги Описание Процесс выработки идеи и превращения ее в несколько сотен страниц Предпосылки Книга должна выть предложена и одобрена Схема, должно быть полностью изложена в деталях и готова для написания книги Постусловия Рукопись произведения будет завершена и готова для издания Этапы работы Разработать план Написаете введения Написание всех глав Написаете резюме Из модели а описаний сразу же можно видеть не только то, кто является пользователями, но также и каковы их взаимодействий с нашей системой написания книги. Обратите внимание, что и издатель, и принтер включены в действие "Печатание книги". Издатель заказывает печатание, а принтер фактически выполняет печатание. Какое вы средство ни использовалось для построения диаграммы действий, важно, чтобы оно могло хранить детальную информацию относительно каждого действия или исполнителя в модели. Хорошие средства от продавцов, перечисленных ранее, позволят нам формировать чрезвычайно полезные сообщения о данных, которые мы помещаем в модель. Я оставляю вас, читатель, далее копаться в UML, по крайней мере, до такой степени, чтобы вы были способны понять модели, которые создадут ваши деловой аналитик н системные архитекторы. Одна из лучших книг по згой теме "Instant UML" (Wrox Press, ISBN 1881000871). Модели данных Имеются два основных тина моделей данных —. логическая и физическая. В течение процесса проектирования СУБД мы сначала строим логическую модель и затем на ее основе — одну или более физических моделей. Вела процесс проектирования идет успешно, полная логическая модель должна быть функционально эквивалентна физической модели(ям), как только будет реализована полная логическая модель. Никакие данные, которые пользователь хочет видать, не должны появиться в физической модели без того, чтобы не быть в логической модели.
Логическая недель представляет оеиоввую ней вменяемую сторону информации, которую клиент должен поддерживать. Логическая меде» может выть реализована разными способами н зависимости от потребностей физической реализации. Независимо от того, используем ли юс SQL Server 2000, Oracle, Access или даже Excel для размещения данных, логическая модель должна остаться неизменной. В процессе логического моделирования мы в первую очередь должны выть убеждены, что каждая часть информации зарегистрирована, так что ее можно будет где-нибудь разместить. Поэтому, когда мы будем в следующих двух главах выполнять процесс нормализации, наша модель будет превращаться из дезорганизованной в значительной степени массы сущностей, с которыми мы начали работать в предыдущей главе, в достаточно организованный набор сущностей. К концу этого процесса мы должны идентифицировать каждую отдельную часть информации, которую мы, возможно, пожелаем физически разместить. Главное различие между логическими и физическими моделями — реализация. Так как мы используем тот же самый язык моделирования, логическая модель будет всегда в значительной степени походить на физическую модель, но в течение стадии логического моделирования лучше всего заставить себя игнорировать слово КАК и полностью сосредоточиться на слове ЧТО войдет в ВД, Физическая модель, которую мы рассмотрим в главе 10, обеспечивает детальный план действий для реализации ВД. Эта модель — то, где мы будем брать сущности, которые обнаружили в предыдущей главе, и превращать их в таблицы. Возможны различные процессы в зависимости от цели и использования данных в ВД, и они будут обсуждены во второй половине книги. В то время как логическая моде» должна быть достаточно полна, чтобы описать коммерческую деятельность, физическая модель данных, которую мы создадим позже, -макет выть подвержена компромиссам между характеристиками и ’ эффективностью. Если эти две модели так сильно отличаются, то в чем цель логического моделирования? Это — серьезный вопрос. Фиксируя, какое должно быть оптимальное размещение данных с точки зрения логичности, мы увеличиваем нашу способность создать более хорошую физическую ВД, даже если должны отклониться немного (ш много) от логической модели. Логическая модель остается важным документом для более поздних изменения в системе и не будет чрезмерно извращена деталями размещения данных, Сущности В стандарте IDEF1X сущности (которые являются синонимом таблиц) моделируются прямоугольниками, что используется в большинстве методологий моделирования данных. Имеются два различных типа сущностей, которые мы моделируем: идентификаторо-независимый и идентификаторо-зависимый, также часто упоминаемые как независимый (independent) и зависимый (dependent) соответственно. Тип сущности Независимая сущность называется таким образом потому, что она не имеет никаких зависимостей первичного ключа от любой другой сущности или, другими словами, нет никаких внешних или мигрирующих ключей в первичном ключе. В главе 3 мы обсуждали внешние ключи, но IDEF1X вводит лпсп~нитмьвь;й термин, который является несколько запутывающим, нс- более иллюстративным, чем внешний ключ, — мигрирующий. Термин "мигрирующий" является обычным и включен в описание,
но вто может ввести в заблуждение, так как мигрировать означает двигаться. Вместо того чтобы фактически передвигаться, он относится к первичному ключу одной сущности, "мигрируя" (копируется) как атрибут в другой сущности, таким образом, устанавливая отношения между этими двумя сущностями. Следовательно, сущность "независима" от любых других сущностей, Вее атрибуты, которые не мигрируют, являются собственными, поскольку они имеют свое происхождение в текущей сущности. Независимая сущность отображается прямоугольником с незакрепленными углями следующим образом: Independent I Здесь Independent — сущность *Независимая!1, Зависимая сущность противоположна независимой сущности, поскольку она будет иметь первичный ключ другой сущности, которым мигрировал в ее первичный ключ. Мы будем редко завершать работу, используя -зависимые сущности на стадии физического моделирования проекта. Это связано с тем, что первичный ключ сущности не должен Выть изменяем в БД, и, конечно, не иметь зависимые сущности, иа которые он ссылается, Конечно, коидепция каскадной коррекции существует, так что это не такая уж серьезная проблема, ио мы увидим позже в главе 10 трудности, характерные для формирования сущностей таким образом. Мы рассмотрим более глубоко идею независимых и зависимых сущностей в разделе этой главы, посвященном идентифицируемым и неидентифицируемым отношениям, Зависимая сущность будет изображаться прямоугольником с закругленными углами: Dependent Здесь Dependent — сущность "Зависимая". То, что мы здесь видим, немного напоминает ситуацию яйца и цыпленка. Зависимая сущность зависит от некоторого типа отношений. Последующий раздел, посвященный атрибутам, содержит информацию, которая основана на некоторых типах отношений, которые мы еще не рассмотрели. Однако ясно, что мы не можем ждать, пока рассмотрим. отношения, чтобы- представить материал, необходимый для завершения-рассмотрения сущностей. Кела Вы впервые рассматриваете модели данных, эту главу может потребоваться перечитать, чтобы получить полную картину, поскольку концепция независимых и зависимых объектов связана с отношениями.
Обозначение Хотя описание IDEF1X не дает правила, как называть сущности, при формировании сущностей мы должны упомянуть этот момент. Один из наиболее важных аспектов проектирования или реализации любой системы — то, как вы называете ваши объекты, переменные, и т. д. Обозначение объектов ВД в этом яе отличается, и возможно более важно ясно назвать объекты ВД, чем другие объекты программирования. Названия, которые вы даете вашим сущностям (и атрибутам также), будут переведены в названия сущностей, которые тождественно будут использоваться программистами и пользователями. Логическая модель будет рассматриваться вами как первичное схемное решение того, как БД была задумана, и должна выть живым документом, который вы изменяете перед изменением любых физических структур. Несколько основных руководящих принципов для обозначения сущностей: ; О Наажаявя сущностей никогда не должны выть во множественном числе. Первопричина этого — то, что название должно обращаться к экземпляру смоделированного объекта, а не к совокупности объектов. Кроме того, звучит глупо, если сказать, что вы имеете "запись автомобилей" ( запись (record) — конструкция, используемая в программировании. Прим, иерее.). Нет, вы имеете запись автомобиля, Для двух автомобилей вы имели бы две записи автомобиля. (Однако, обратите внимание, что системные таблицы в SQL Server все во множественном числе, которые я лично имитировал в период моего становления.) □ Даваемое название должно непосредственно соответствовать содержанию сущности. Например, если вы моделируете запись, которая представляет человека, назовите ее person (человек). Если вы моделируете автомобиль, назовите зха automobile (автомобиль). Обозначение — не всегда урезанное и сухое, но благоразумно иметь названия простые и по сути. Часто названия сущностей должны быть составлены из нескольких слет. Конечно, допустимо включать в названия пробелы, когда нужно использовать несколько слов, но это не обязательно. Например, сущность, которая хранит адреса людей, могла бы быть названа: Person Address, Person_Address w, используя стиль, ж которому я недянно привык, и тот, которому мы будем следовать в этой книге, — personAddress. Общее название этого типа обозначения — "нотация верблюда" или смешанный случай. Обратите внимание, что мы находимся на логической стадии моделирования. Мы вообще хотели бы избежать использования пробелов в названиях наших физических структур, Хотя и допустимо иметь названия с пробелами, ото не очень хорошо с точки зрения удобства. В SQL Server вам потребовалось бы использовать яти названия, окруженные квадратными скобками, [подобно этому примеру]. Никакие сокращения не должны использоваться в логических именах сущностей. Каждое слово должно быть полностью понятно. Аббревиатуры имеют тенденцию запутать смысл. Однако сокращеииж могут быть необходимы в физической модели из-за некоторого стандарта обозначений, который вынуждает вас это делать. Если вы используете сокращения, то должны гарантировать, что описатель каждый раз использует одно и то же сокращение. Это — одна ив основных причин избегать сокращений, чтобы ие получить описатели, названные "description", "descry", "desc", "descrip" и "descriptn", все обозначающие описатель description. О физических обозначениях будем дополнительно говорить главе 10. Одаако небольшое предупреждение: важно не использовать дм J—1868 111
сущностей длинные описательные названия и виде целых предложений типа . leftHandedMUtensLostByKittensOnSaturdayAfternoons (рукавицы с левой руки, затерянные котятами в субботу после полудня) за исключением случая, когда эта сущность должна отличаться от leftHandedMittensLostByKitteneOnSundayMornings (рукавицы с левой руки, затерянные котятами в воскресенье утром) — усечение на экране затруднит чтение названия. Лучшим названием могло вы выть mittent (рукавицы) или даже lostMtttene (потерянные рукавицы). Многое Из того, что иодируется в большом назваийи, будет, вероятно, само по себе сущностями: Mittens (рукавицы), Mitten Status (состояние рукавицы). Mitten Hand (с какой руки рукавица). Mitten Used By (рукавица, используемая для Kittens (котята), и т. д. Одаако это больше связаво с йонятиём нормализации, и будет обсуждено далее и главах в и Т. Атрибуты Атрибуты сущности должны иметь уникальные имена внутри нее. Они представляются в виде списка имен внутри прямоугольника сущности: AtMMeExsmpfe Attribute 1 Attn саде 2 Здесь AttributsBxample -= сущность "Пример атрибутов"; Attribute — атрибут. Замечание: формально, это неправильная сущность, так как не имеет определенною первичного ключа (как требуется в IDEF1X). Мы рассмотрим ключи в следующем разделе. На данном этапе мы просто ввели бы все атрибуты, которые определили яа стадии разработки. Практически, было бы удобно объединить процесс выявления сущностей и атрибутов с начальной стадией моделирования. Все будет зависеть от того, сколь хорошие средства вы используете я своей работе. Большинство средств моделирования даиных обеспечивает быстрое построение моделей и хранение обширной информации для документирования их сущностей и атрибутов. На ранних стадиях логического моделирования может быть весьма большое различие между атрибутом я столбцом. Как мы увидим в следующих двух главах* определение, что атрибут может хранить, изменится совсем немного. Например, атрибуты человека могут начинаться просто с адреса и номера телефона. Однако, как только мы это нормалиауем, то разобьем их на много столбцов (адрес — на номер квартиры, название улицы, город, штат, почтовый индекс и т. д.) и, возможно, ва много других сущностей. Первичный ключ Как мы отметили в предыдущем разделе, сущность в ШНИХ должна иметь первичный ключ. Это удобно для нас, так как в главе 3 мы определили, что-для кортежей или сущности каждая запись должна быть уникальна. Первичный ключ может быть единственным атрибутом, или ои может быть составным ключом (овределенным ранее как ключ е несколькими полями), и должны быть значения для всех атрибутов ключа (на физическом языке, в первичном ключе не допускаются значения Null), Обратите внимание, что никакое дополнительное обозначение не требуется, чтобы указать, что величина является первичным ключом.
Первичный ключ обознатаетея помещением его атрибутов выше горизонтальной линии, проходящей через прямоугольник сущности: ( Primary Key Example Primary Key Example Primary Key Attribute 1 Attribute 2 1 Здесь Primary Key Example — сущность “Пример первичного ключа"; Primary Key — первичный, ключ; Attribute — атрибут. На ранней стадии логического моделирования я обычно не люблю назначать какие-либо значащие атрибуты перяичиого ключа. Главная причина этого состоит, в том, чтобы сохранить чистую модель. Я обычно добавляю абстрактный первичный ключ, чтобы перейти к другим сущностям; это позволяет мне видеть, кто чем владеет. Как мы увидим позже в этой главе, абстрактный первичный ключ будет содержать название сущности, так что, когда мы создаем отношения, которые вызывают миграцию ключа, независимо от того, какие первичные ключи мы выберем, они будут мигрировать к сущности-потомку в отношении. Поскольку, вероятно, мы не выберем первичный ключ, который будет в конечном счете реализован-, я обычно моделирую все- потенциальные ключи (или уникальные идентификаторы) как вторичные ключи (иепераичные уникальные ключи). Результатом является то, что в логической модели становится ясно, какие сущности играют роль собственника по отношению к другим сущностям. Это, конечна, не является требованием логического моделирования, а отражает мое собственное предпочтение, которое я счел полезным методом документирования и который оставляет модель чистой и соответствует моему методу реализации, рассмотренному позже. Использование естественного ключа в качестве первичного ключа на логической стадии моделирования не только допустим), но и предпачитаетея.многими архитекторами. Я попытался приравнять все Ши логические объекты абъектноюриентировшшым классам, которые идентифицированы не первичным ключом, а, скорее, указателем. Вторичные ключи Наша следующая тема посвящена вторичным ключам. Как было предварительно определено в главе 3, вторичные ключи — набор одного или большего количества полей, чью уникальность мы хотим гарантировать по всей сущности. Вторичные ключи не имеют специальных обозначений, как и первичные ключи, и не мигрируют при любых отношениях. Они изображаются на модели очень простым способом; Alterne te Key Example ' _ Primary Key Alternate ёгч (AKI) Alternate Key 2 Att ' (AK2) Alternate Key 2 An г (Ak2) Здесь Alternate Key Example — е^щноешь “Пример вторичных ключей"; Primary Kay. — первичный ключ; Alternate Key— вторичный ключ; Att — атрибут.
В этом примере имеются две вторичные ключевые группы-, группа AKI (Alternate Key 1), которая имеет я качестве своего члена один атрибут, и группа АК2 (Alternate Key 2), которая имеет два атрибута. Одно расш прение, которое использует ERwirt (средство моделирования данных, созданное Computer Associates: http://ca.com/products/alm/erain.htm), показано ниже: fetemate Key Example _ Primary Key Memae Кед (AK1.1] Alternate Кед 2 Ай 1 (AK2.T) Alternate Кед 2 AH 2 (№2.2) Обратите внимание, что имеется дополнительное обозначение <нолгвр позицию*, прибавляемое к АК1 и АК2, чтобы обозначим, положение атрибута в ключе, Формально в логической модели ату информацию ие следует доказывать, и, конечно, она должна игнорироваться. Логически не имеет значения, который атрибут используется первым в ключе. Когда ключ будет физически реализовываться, ото будет интересным только по причинам самой реализации. Внешние ключи Виеияие ключи также называются мигрирующими атрибутами. Они являются первичными ключами сущности, которые служат как указатель на экземпляр информации в другой сущности. Эти ключи снова являются результатом отношений, которые мы рассмотрим в следующем разделе. Внешние ключи обозначаются как вторичные ключи е добавлением символов "FK” (Foreign Key) после внешнего ключа, например. Foreity Кед Exanyte Primary Кед______________ Foreign Кед УК) ' i \ _ ________________________. Збесь Foreign Key Kxemple — сущность "Пример внешнего ключа"; Primary Key — первичный ключ; Foreign Key — внешний ключ. Диаграмма не показывает, на какой сущности мигрирует ключ, и может возникнуть неразбериха в зависимости от того, как вы выберете ваши первичные ключи. Это — ограничение всех методологий .моделирования, поеколыгу было бы излишне запуганным для процесса, если бы мы покмали название сущности, откуда берется ключ, по нескольким причинам: □ Нет никакого предела (да и не должно быта) тому, как далеко ключ будет мигрировать от своего первоначального владельца. □ Это также неблагоразумно потому, что один и тот же атрибут йог бы мигрировать из двух разных сущностей, особенно на ранних стадиях процесса логического проектирования. Это — одна из причин, чтобы для схемы первичного ключа, которую мы создадим в нашей логической модели, просто выбирать название <entityName>l<i (<имя_сущности>тц) как имя ключа для данной сущности. По моему мнению, такое название сущности легко опознаваемо и более ясное.
Домены Домен (Domain) — термин, который мы, к сожалению, собираемся использовать в двух очень близких контекстах, В главе 8 мы узнали, что домен — набор допустимых значений для атрибута. В IDEF1X домены имеют тонко отличающееся определение, которое охватывает предыдущее определение, но с полезной характерной особенностью. В этом случае домены — механизмы, которые не только позволяют нам определять допустимые значения, которые могут быть размещены в атрибуте, но также и обеспечивают вид наследования в определениях наших типов данных. Вот примеры: □ String (строка) — строка символов. □ SocialSecurityNumber (номер социального страхования) — набор символов с форматом Q Positiveinteger (положительное целое) — целое число, которое предполагает домен от О до max (целое число). Далее мы можем создать подтипы, которые унаследуют значения исходных доменов. Мы будем строить домены для любых атрибутов, которые регулярно используем, а также домены, которые являются шаблонами для нечасто используемых атрибутов. Например, мы могли вы иметь основной домен типа символа, где определяем, что нужны не все символы. Мы могли бы также определить домены, названные пате (название) и description (описание), для использования во многих сущностях, и определить их, как это нам потребуется, При логическом моделировании требуется только несколько видов информации, которую мы, вероятно, будем хранить, таких как обычные типы атрибутов — символ, числовой, логический, или даже двоичные данные. Мы должны хранить эти домены как реализацию описания независимых типов данных. Например, мы могли вы определить домен:' □ GloballyUniqueldentifier (GUID — глобальный уникальный идентификатор) — значение, которое будет уникальным независимо от того, где оно сформировано. В SQL Server мы могли бы использовать uniqueidentifier (GUID-аначение), чтобы реализовать этот домен, В Oracle, где нет точно того же механизма, мы реализовали бы это по-другому; то же самое было бы истинным, если мы используем текстовые файлы для хранения данных. Когда мы начнем физическое моделирование, то будем использовать те же самые домены, чтобы унаследовать также и физические свойства. Это — реальное достоинство использования доменов. Создавая для атрибутов шаблоны многократного применения, которые будут также использоваться, когда начнем создавать столбцы, мы уменьшим количество усилий, которые потребуются, чтобы строить простые сущности, составляющие большую часть нашей работы. Это также позволят создать стандарты компании, многократно использун те же самые домены во всех наших общих моделях. Позже физические детали, такие как тип данных, ограничения и возможность не иметь значения, будут выбираться только из нескольких основных физических свойств, которые можно наследовать. Так как нам нужно иметь намного меньше доменов, чем реализованных атрибутов, мы получим двойную выгоду быстрой н последовательной реализации. Одаако мы не можем использовать механизмы наследования, когда строим нашв «блицы вручную. Реализация доменов строго основана на используемых программных средствах.
Например, мы могли бы определить следующую иерархию: Здесь String' (строка) — базовый домен, от которого мы затем получимifaae (название) и Description (описание). Filename (имя файла), FirstName (имя) и 1 ае ; .’.’ая» (фамилия) являются наследниками Name (название). На стадии логического моделирования это может показаться работой впустую, потому что большинство этих доменов будет обладать некоторыми основными деталями типа запрета использования NULL или незаполненных данных. Однако вам не всегда может потребоваться Filename, в то время как LastName — практически всегда. Важно реализовать домены для. возможно большего числа различных типов атрибутов в случае, если вы находите правила или типы данных, которые являются общими для любых дометав, которые вы задали. Домены — одна из наиболее захватывающих особенностей HMSF1X, Они обеспечивают легкий метод создания стандартных атрибутов, сокращения как промежутка времени, требуемого для этого создания, так и числа ошибок, которые возникают при этом. Специальные программные средства реализуют домены, обеспечивая определение и наследование большинства свойств цепочки доменов, что делает создание ВД более легким. Очевидно, что если вы создаете ваши ВД и модели вручную, менее вершин®, что вы реализуете ваши таблицы, дапользуя наследование доменов. Домены или типы данных можно показать у сущности справа-от названия атрибута следующим образом: domainExample atttibuteName: domemName’ I attnbuteNameZ: domainName Здесь domain£xample — сущность “Пример домена": attributeA'ame — имя атрибута: domalnNama — имя домена. Так, если мы имели сущность, которая содержит значения домвнов для описания типа person (человек), мы могли бы сформировать модель: регзолТуре ре re only ре id: primaryKey гите: пате description description I Здесь person Type — сущность “тип-человек"; personTypeld — идентификатор типа-человека; name — имя; description — описание. не
В атом случае мы имеем три домена: □ PrimaryKey — является указателем строки для мигрирующего внешнего ключа, Реализация ие подразумевает построения домена, так что мы можем выбрать это значение любьпя образом, как пожелаем. □ Name — домен общего вида для хранения имени экземпляра сущности. Используя его как наш стандартный домен имени всюду, где мы хотим пэмес-тить название, мы обеспечим сотласован ностъ. Если имя дайной сущности ие соответствует атому образцу, мы можем создать новый домен. □ Description — тот же самый тип домена, что и домен имени, только хранит описание. Обозначение Обозначение атрибута несколько более интересно, чем обозначение сущности. Мы смазали, что название сущности никогда не должно быть во множественном числе. Тот же самое формально истинно и для атрибутов. Однако в этом месте процесса моделирования мы будем все же использовать названия атрибутов во множественном числе. Использование названия во множественном числе может быть хорошим напоминанием, что мы ожидаем несколько значений. Например, мы могли -вы иметь сущность-.Person (человек! с идентифицированным атрибутом Children (дети). Сущность Person идентифицировала вы единственного человека, а атрибут Children идентифицировал бы сыновей и дочерей этого человека. Стандарты обозначения атрибутов всегда были весьма горячей темой е несколькими различными схемами обозначений, разработанными аа прошлые 30 лет еозляния ВД. Мы рассмотрим это в главе 10, когда начнем реализовывать нашу ВД. Стандарт обозначений, которого мы будем придерживаться, очень прост. □ Обычно не нужно повторять название сущности я названии атрибута, за исключением первичного ключа. Название сущности подразумевается включением атрибута в сущность. Так- как первичиый ключ будет мигрировать к другим сущностям, эта маленькая уступка делает миграцию более простой, поскольку мы не должны переименовывать каждый атрибут после миграции, не говоря уже о создании присоединения к устройству очистки памяти в SQL. □ Выбранное название атрибута должно отражать точно, что содержится в атрибуте и как это относится к сущности записи. □ Как н у сущностей, никакие сокращения не должны иеполмоватаея в логическом обозначении атрибутов. Каждое слово должно быть записано полностью. Если какое-то сокращение все же должно использоваться в каком-то месте из-за некоторого стандарта ббояначвний, то должен быть какой-то способ, чтобы удостовериться, что оно используется последовательно, как было рассмотрено ранее в агой главе. Последовательность — ключ к надлежащему обозначению атрибутов, так что если у вас или у вашей организации нет стандарта назначения имен, стоило вы его разработать. Мой принцип задания имен — они должны быть простыми и удобочитаемыми, избегая сокращений. Этому стандарту мы будем следовать и при логическом моделировании, и на физической стадии. Независимо от того, каков ваш стандарт, задание образцов обозначения будет делать ваши модели легкими для работы и для вас самих, и для ваших программистов и пользователей. Любой стандарт всегда лучше отсутствия какого-либо стандарта.
Другой шаг, который вы должны сделать — это спросить о стандартах обозначений Вашего клиента (или работодателя), чтобы обеспечить будущее взаимопонимание, а не создавать новые стандарты. Отношения До сих пор мы рассматривали конструкции, в значительной степени одинаковые при всех методологиях моделирования данных. Сущности всегда обозначались прямоугольниками, и атрибуты — словами в этих прямоугольниках. Однако отношения — совсем другой вопрос. Каждая методология дает свои отношения. IDEF1X весьма отличается от всех других прежде всего хорошим способом представления. Причина, по которой я предпочитаю ПЖР1Х — его способ цредставлеиия отношений, Чтобы понять это, возьмем термины parent (передок) и child (потомок) и пример отношения между «ими. Из глоссария в описании IDEF1X мы находим следующие определения: □ Entity, Chili (сущность, потомок) — сущность в определенных отношениях связи, чьи экземпляры могут быть ввязаны с нулем ига одним экземпляром другой сущности (сущность-предок). □ Entity, Parent (сущность, предок) — сущность в определенных отношениях связи, чьи экземпляры могут быть ввязаны с множеством экземпляров другой сущности (сущность-потомок). О Relationship (отношение) — связь между двумя сущностями или между экземплярами одной и той же сущности. Это настолько ясные определения, что заслуживают быть принятыми па государственном уровне. Каждое отношение обозначается линией, проведенной между двумя сущностями со сплошным кружком на одном конце этой линии. Первичный ключ предка мигрирует к потомку. Это представляет то, как мы обозначаем внешний ключ на модели. Мы попытаемся пройти все различные структуры в IDEFlX-методологии, сопровождая их кратким обзором некоторых других методологий, что будет необходимо нам для понимания, так как не каждый будет использовать IDEF1X. Имеется несколько различных типов отношений, которые мы рассмотрим: □ Иуитцфхциручэщрр □ Неидснтифицирукицес □ Необязательное
□ Рекурсия О Классификация или подтипы □ Многие ко и#пм Здесь Table — сущность "таблица”; Tree Entity — сущность "дерево”. Один ко многим Отношение один ко многим" является на самок деле примером неправильного употребления, и может рассматриваться как отношение "один к любому числу". Ойо охватывает отношения "один, к нулю", "один к одному", "один ко многим” или, может выть, "один к точна п’". Мы увидим, что формально более точно = ото "один к (от ш до п)"; так мы можем определить многие очень точные (или очень свободные) термины, как диктует ситуация. Однако более привычный термин — "один ко мяогим", и мы не будем делать уже запутанный термин еще более запутанным. Имеются два основных тапа отношения "один ко многим" —идентифицирующее и (неудивительно) неидентифицирующее. Различие, как мы увидим, определяется тем, мигрирует ли первичный ключ. Имеется очень немного разновидностей отношения "один ко многим”, и мы рассмотрим все их (и их обозначения) в этом разделе. Идентифицирующие отношения Идентифицирующее отношение указывает, что атрибут мигрирующего первичного ключа мигрирует в первичный ключ потомка следующим образом ftrent Chid Pare-t Pr-rjy Кк ChMPnmay Parent Primary (FK) Attribute I Attribute 2 Attn Du te 1 \ Attribute 2 ЗЗесь Parent — сущность "предок”; Child — сущность "потомок”; Parent Primary Key — первичный ключ предка; Child Primary Key первичный ключ потомка; Attribute — атрибут.
Обратите внимание, что сущность-потомок в отношении изображена как прямоугольник с закругленными углами, что означает зависимую сущность. Причина, что оно называется идентифицирующим отношением, заключается в том, что мы должны будем иметь экземпляр предка, чтобы иметь возможность определить запись экземпляра-потомка. Существование экземпляра-потомка (определяемого как "характерные или необходимые свойства, которые служат, чтобы определить или идентифицировать кое-что") определяется существованием предка. Другими словами, идентификация и определение записи-потомка основаны на существовании записи-предка, Пример — заказ на поставку и его элементы. Без заказа на поставку элементы не имеют никакого смысла. Неидентифицирующие отношения Неидентифицирующее отношение означает, что атрибут первичного ключа не мигрирует в первичный ключ потомка. Оно используется более часто, чем идентифицирующее отношение. В то время как идентифицирующие отношения были основаны на необходимости существования предка, что определяет даже само существование потомка, неидентифициру ющее отношение — (что не удивительно) полная противоположность. Взяв пример заказа на поставку, рассмотрим продавца изделия. Он не определяет существование элементов заказа. Продавец в этом случае может быть, а может и не быть, так как бизнес-правила могли бы определять, но обычно они это не делают, является ли отношение идентифицирующим или неидентифицирующим: сами данные будут фундаментальными свойствами отношения. Неидентифицирующие отношения моделируются пунктирной линией: Parent Child Здесь Parent — сущность "предок"; Child — сущность "потомок". Почему вам следует использовать идентифицирующие вместо иеиденмфицирующих отношений? Причина фактически довольно проста. Ясли сущность-предок (как мы отметили в предыдущем разделе) определяет сущность-потомок, то мы будем использовать идентифицирующее отношение. Если же, с другой стороны, отношение определяет один из атрибутов потомка, то мы используем неидентифицирующее отношение. Кек другой характерный пример рассмотрим следующее: □ Идентифицирующее — Пусть есть сущность, которая хранит партнеров, и сущность, которая хранит номера телефона партнеров. Партнер (contact) определяет номер телефона, и вез партнера не имелось вы никакой необходимости в номере телефона партнера ( contact PhoneNumber). Contact - . j ContactPhoneNumber
□ Неидентифирующее— Возьмем сущности, которые мы определили дем идентифицирующего отношения, вместе с дополнительной сущностью, которую назовем contactPhoneNumberType (тип номера контактного телефона). Эта сущность связана е сущностью cantactPhoneNumber, но неидентифидирующим способом, и определяет набор возможных типов номеров телефонов ("Голосовой", "Факс" и т. д.), каким мог бы выть cantactPhoneNumber. Тип номера телефона не определяет номер телефона, а просто классифицирует его, ContactPhoneNumbertype Сущность contactPhoneNumberType обычно называется сущностью домена или таблицей домена. Вместо того чтобы иметь фиксированный домен для атрибута, мы проектируем сущности, которые позволяют программно изменять домен без перекодирования ограничений и кода клиента. Хак дополнительное преимущество, мы. можем добавлять столбцы, чтобы определять, описывать и расширять значения домена для обеспечения бизнее-правил. Это также позволяет пользователю клиента строить для других пользователей списки, чтобы выбирать значения с очень небольшой долей программирования. Имеются два различных класса нвиденти-фицирующих отношений — обязательное и неовязательное. Ми поподробнее рассмотрим их в следующих двух разделах. Обязательное отношение Обязательные иеидентифицируйщие отношения называются таким образом потому, что требуется мигрирующее поле в экземпляре потомка. Когда реализуется это отношение, мигрирующий ключ должен быть помечен как НОТ NULL (не является NULL>. Jarent Child Parent ftimary Gftf Primary Key ' Attribute 1 Attribute 2 Parent Primary Key (FKJ Atttfcute 1 Апноме 2 Здесь Parent — сущность "предок"; Child — сущность "потомок"; Parent Primary Key — первичный ключ предка; Child Primary Key — первичный ключ потомка; Attribute — атрибут.
Обратите внимание, что сущностьпотомак в атом примере не имеет закруглений у прямоугольника.. Это потому, что обе сущности независимы, так как ни один из атрибутов первичного ключа потомка не является внешним ключом. Другой пример такого отноше ния мог вы выть явят из ВД проката кинофильмов: Здесь genre — сущность "жанр”; movie — "кинофильм". Отношение можно было бы представить в виде Genre <класскфииярует> Movie, где сущность Genre — сущность "один", a Movie — "многие". Обязательно каждый арендуемый кинофильм должен иметь жанр, чтобы их можно выло расставить по полкам. Необязательное отношение Нам не всегда нужно, чтобы потомок имел значение мигрирующего ключа. В этом случае мы зададим мигрирующий ключ потомка необязательным, для которого в этом случае разрешается значение пустого указателя ( NULL). Так как неидентифицирующее отношение означает, что предок — атрибут потомка, то это эквивалентно наличию необязательного атрибута (атрибут, который может иметь значение NULL). Мы показываем это пустым ромбом на противоположном конце линии с черным кружком, как изображено ниже: Parent Child I Parent Primary fay Child Primary Key I Attribute 1 I Attribute 2 Parent Primary Key (F K) Atnibute 1 Attribute? Здесь Parent — сущность "предок"; Child — сущность "потомок"; Parent Primary Key — п^вичньш ключ предка; Child Primary Key — первичный ююч потомка; Attribute — атрибут. Если вы задаетесь вопросом, почему не может выть необязательного идентифицирующего отношения, то потому, что вы не можете иметь никаких необязательных атрибутов в первичном ключе; это справедливо и для IDEF1X, и для SQL Server 2000. В качестве необязательного отношения "один ко многим" рассмотрим следующую структуру; Здесь invoice — сущность "счет-фактура"; inviocebineltem — сущность "элемент счета-фактуры": discountType — сущность "тип скидки".
Сущность 1 .сvox ceLrneltern содержит элементы счета-фактуры, определяющие оплату. Далее рассмотрим ситуацию, когда мы в некоторых случаях применяем стандартные скидки к элементу счета-фактуры. Тогда отношение от invoiceLinelten к сущности discountType — необязательное. Мощность отношения Мощность отношения означает число экземпляров потомка, которое может быть помещено для каждого предка этого отношения. Следующая таблица показывает шесть возможных мощностей, которые могут быть у отношения. Parent Child □------------------€3 Одаи к нул ю или большему числу Parent Child ее-----------------о Один к одному или большему числу Parent Child ЕЗ---------------тКЕЗ Один к нулю или одному (не больше, чм» к одному) Parent Qti О-----------------dZ3 Один к {от четырех » восьми) Parent 0116 Один к точив N {здесь в означает, что предав имеет точно 5 потомил) parent ChM <запись> Специальная амись, описывающая мощность Здесь Parent — сущность "предок"; Child — сущность “потомок". Возможное отношение "один к ©даому или большему числу" могло вы использоваться, чтобы представить отношение между опекуном (guardian) и учащимся (student) в школьной ВД: Мне нравится этот пример, потому что он выражает потребности чрезвычайно хорошо. Он говорит, что если запись guardian существует, то должен существовать student, но для нас не требуется, чтобы запись student имела поле guardian для размещения данных о нем.
Другой пример, который я обычно люблю рассматривать при обсуждении вопросе мощности отношения, следующий. Рассмотрим клуб, который имеет членов (Member) и некоторые положения (Position), которые они должны или могли аанять: В примере А мы говорим, что член клуба может занять столько положений, сколько их имеется, Во втором случае мы говорим, что он может занять по крайней мере одно положение. в последнем случае — 0,1 или 2. Все они выглядят одинаково, ио Z или 0-2 используются чаще. Лично я рассмотрел, включая примеры, все из этих типов мощностей, ао в большинстве случаев это либо слишком трудно, либо слишком глупо, так что мы просто упомянули несколько более типичных. Однако очень хорошо иметь их под рукой, если они станут необходимы. Функциональные имена Функциональное имя — альтернативное название, которое мы даем атрибуту, когда используем его как внешний ключ. Цель функционального имени состоит в гои, что мы иногда должны разъяснять использование мигрирующего ключа, потому что ии сущность-предок очень общая, и мы хотам дать более определенное название, или мы имеем мжуенфатиав отношения от той же самой сущности. Так как названия атрибутов должны быть уникальны, яам нужно назначить различные названия для записет потомка. В нашем приме!»: Parent Parent Primary Key Child_____________4 4__________________ ChiM Primary Key_______________ Parent Primary Key 2.Parent Primary Key (FK) Parent Primary Irey 1 .Parent Primary Key (FK) Здесь Parent — сущность "предок": Parent Primary Key — первичный ключ предка: Child — сущность "потомок"; Child Primary Key — первичный ключ потомка.
... мы имеем два отношения от сущности-предка к сущности-потомку, и мигрирующие атрибуты были названы как Parent Primary Key 1 (первый первичный ключ предка) и Parent Primary Key 2 (второй первичный ключ предка). В качестве примера пусть у вас есть сущность User (пользователь), и вы хотите разместить name (имя) или Id (идентификатор) пользователя, которые формируют сущность Object (объект), Мы получили вы ситуацию наподобие следующей: Обратите внимание, что мы имеем два отношения к сущности Object от сущности User. Один назван (Created For Primary Key] (созданный для первичного ключа), и другой (Created By Primary Key] (тлдянный первичным ключом). Этот пример также показывает, почему вы поместили вы название сущности в атрибут первичного ключа. Когда первичный ключ мигрирует к другой сущности, важно знать, какова первоначальная сущность-предок, Так как атрибут в сущности Object имеет название [Primary Key] (первичный ключ), мы вы имели название первого атрибута таким же, -как и второго. Другие типы отношений Имеется несколько других, менее важных типов отношений, которые не используются так уж часто. Однако они чрезвычайно важны для понимания. Рекурсия Одно из более сложных для реаливации отношений, но одно из наиболее важных — рекурсивное отношение, известное также как самоприсоеднняюшееся, иерархическое, самоссылающееся или самоотносящееся. Оно моделируется неидентифицирующим отношением не к другой сущности, а к севе самой. Мигрирующему ключу отношения дают функциональное имя (и я обычно использую соглашение по обозначению, добавляя "parent" (предок) в начале названия атрибута, ио это не обязательно). Здесь Tree Entity — cywHoc/nb-depeso.
Рекурсивное отношение удобно для создания древовидных структур, как в следующей структуре организации; Здесь ЛИ — вся организация; IT —'подразделение информационных технологий; HR — отдел кадров; Marketing — подразделение маркетинга; Programing — отдел программирования; Database Management — отдел менеджеров БД. Чтобы объяснить эту концепцию, нам нужно рассмотреть данные, которые размещаются с помощью следующей иерархии: Здесь organisation — сущность "организация". Мы используем organlsationNome (название организации) в качестве первичного ключа и parentOrganizationName (название организации — предок) как мигрирующий ключ, атрибут с функциональным именем, который задает само-ссылку соединения: Organ!aat1опиаие parentOrganiMtionNaine All IT HR Marketing ₽rogra»ing Database Management <null> All All All IT IT Диаграмма организации может теперь выть восстановлена, начиная с "АН" (вся организация), и получая его первого потомка — "IT" {подразделение информационных технологий). Далее мы получаем первого потомка “IT” — "Programming" (отдел программирования), "Programming" ие имеет никаких потомков, поэтому мы возвращаемся к "И"’ и получаем следующего потомка — "Database Management" (отдел менеджеров ВД) и т. д. Рекурсивное отношение так названо потому, что популярный алгоритм для реализации таких структур на функциональных языках программирования использует рекурсию, чтобы организовать процесс, который мы моделируем в нашем примере.
В качестве заключительного примера рассмотрим сущность person (человек), где мы храним имя, адрес я ч. д. людей, которых мы включаем. Вели юл хотим построить эту сущность с возможностью указать ва супруга (spouse), то могли вы спроектировать следующее: person_____________ personld spouseFfersonld (FK) । ¥ \ I I I_____________I являет» супругом Здесь регвшМ — идентификатор человека; spousePersonM — идентификатор супруга. Заметьте, что мы задаем его как отношение "один к нулю или одному”, так как (для большинства регионов) человек может иметь только едивствениого супруга (супругу)- Отношения классификации Отношения классификации (также упоминаются как подтипы) — Другой специальный тип отношения "один к нулю или одному", используемые, чтобы указать, является ли одна сущность специфическим типом другой общей сущности. Обратите также внимание, что нет никаких черных точек на концах линий; специфические сущности изображены прямоугольниками с закругленными углами; это показывает, что овм действительно зависят от общей сущности. При использовании этого типа отношений мы имеем три разные части: □ Generic Entity (общая сущность) — это сущность, которая содержит все атрибуты, являющиеся общими для всех сущностей-подтипов. □ Discriminator (дискриминатор) — это атрибут, который действует как ключ, чтобы определить сущность, где размещена дополнительная, более определенная информация. □ Specific Entity (специфическая сущность) — это место, где размещена специфическая информация, и которое определяется дискриминатором.
Например, рассмотрим библиотеку видеофильмов. Если вы хотите хранить информацию о каждом из видеофильмов, которые у вас имеются, независимо от их формата, то могли бы построить отношение классификации етедуияцим образом: Эти типы отношений обычно называются отношениями "является" — является VHC-видео (для бытового видеомагнитофона } или является DVD-видео (цифровой диск}. Таким образом, вы могла вы хранить название, фамилии актеров» длину и, возможно, описание содержания в сущности Video (видеофильм), а затем иа основе дискриминатора format (переключатель форматов) вы могли вы разместить информацию, которая оляется специфичной для EHS-лент или DVD в своих собственных отдельных сущностях, вроде специальных особенностей и меню для DVD, медленной или быстрей скорости для FHS-летт. Имеются два различных типа категорий — полные и неполные. Полный набор категорий моделируется двойной линией даскриминатора, а иеполвыв — одной. Полный набор категорий Неполный набор категорий Основное различие между подиыми и неполными категориями заключается в том, что в полных отношениях классификации каждый общий экземпляр должен иметь один специфический экземпляр; в неполном случае это не обязательно. Например, мы могли бы иметь полный набор категорий подобно следующему:
Отношение читается следующим образом “Italoyee (служащий)Л!Ляевм выть или Male (мужчина), или Female (женщина)". Это, конечно, полный набор категорий, так как не существует никакого другого пода (вех). Однако возьмем следующий набор категорий Employee Q Salaried Hourly В атом случае мы имеем неполный подави, потому что Employee (служащий) является или salaried (находящимся на ставке), или hourly (почасовиком), но могут выть и другие категории (type), типа работающих по контракту. Нам может не понадобиться хранить какую-либо дополнительную информацию относительно них, тан что не потребуется специфическая сущность. Это отношение читается как: "Служащий может выть или находящимся на ставке, или почасовиком, или кем-то другим". Многие ко многим Отношение "многие ко многим" известно также как неспецифицированмое отношение, которое является фактически лучшим названием, но гораздо менее известным. Очень часто мы имеем отношения “многие ко многим' в ваших логических моделях. Фактически, чем ближе вы приближаетесь к надлежащей модели ВД, тем больше вы видите, что каждое отношение будет иметь тип "многие ко многим”. На ранних стадиях моделирования полезно определить отношения как "многие ко многим", даже если они будут изменяться во время реализации. Эти отношения моделируются линией с заполненной черной точкой на каждом конце; [ Entity X Entity ¥ te- ...........-——• .. ... _ Здесь Entity — сущность, . .. Обратите внимание, что мы не можем фактически реализовать это отношение в физической модели, так что часто переходим к более реализуемому представлению; Entity_X ~~~ g jty_x_Entity Y : Ent ity_Yf Z>-HZZZZ^HZ1 Здесь промежуточная сущность Entity_X~Entity_Y называется разрешающей сущностью (resolution entity). В налим моделировании мы будем использовать й прежнее представление, когда ие определяются никакие дополнительные атрибуте», чтобы описать отношение, и последнее представление, когда мы эти атрибуты используем.
Чтобы сделать это несколько более ясным, рассмотрим следующий пример: Customer Product Customerto Productld Name Name Здесь Customer — сущность "заказчик", Product — сущность ”изделие': Customerld, Froduceid — соответствующие первичные ключи; Name — имя заказчика или название изделия. В этой ситуации мы установили отношение, где многие заказчики (customer) связаны со многими изделиями (product). Эго — обычная ситуация, поскольку мы редко создаем определенные изделия -дет определенных заказчиков; скорее, любой заказчик может купить любое из изделий. В этом месте моделирования мы использовали бы представление "многие ко многим". Однако если мы обнаруживаем, что заказчик может только быть связан с изделием ж течение некоторого периода времени, мы могли бы выбрать другое представление: На атом рисунке в дополнение к обозначениям предыдущего рисунка Customer Product — сущность " заказчик-изделие'': Productld, Customerld — первичный ключ промежуточной сущности; BeginDa te — начало периода, когда конкретный заказчик связан с конкретным изделием, EndDate — конец этого периода. Фактически в большинстве случаев мы увидим, что отношение ’многие ко многим" будет требовать некоторой дополнительной информации для своего завершения. Глагольные конструкции (обозначения) Отношениям дают имена, которые называются глагольными конструкциями, и которые делают отношение между сущностью-предком и сущностью-потомком удобочитаемым предложением; они включают названия сущностей и мощность отношения. Название обычно формируется от предка к потомку, но может быть выражено и в обратном направлении или даже в обоих нлправлетях, Глагольная конструкция располагается на модели где-нибудь около линии, которая формирует отношение: Отношение следует назвать так, чтобы имя удовлетворяло следующей общей структуре для чтения всего отношения: мощность предка — имя сущности-предка — имя отношения — мощность потомка — имя сущности-потомка
Например, следующее отношение: contact phoneNumbei I ieptwnwfustao будет прочитано следующим образом: One contact is phoned using zero, one or more phoneNumber(s). (Одному партнеру позвонят, используя ноль, один или большее количество телефонных номеров.) Конечно, предложение может иметь (или не иметь) правильный грамматический вид, поскольку возникает вопрос, как партнеру могут позвонить, используя ноль телефонных номеров. Очевидно, если бы мы попробовали сказать это нетехническо.му человеку, то выло вы больше смысла сказать следующим образом: One contact can either have no phoneN umber or may have one or more phoneNumbers. (Один партнер может или не иметь никакого номера телефона или иметь один или большее количество телефонных номеров.) Очевидно, язык моделирования не учитывает лингвистику при формировании этой спецификации, но с технической точки зрения не имеет значения, что партнеру позвонят, используя ноль телефонных номеров, так как из этого следует, что он не имеет никакого номера телефона. Способность читать отношение помогает нам замечать очевидные проожмы, Например, следующее отношение: eontactType contaa ---------\ classifies J------------------ на первый взгляд выглядит нормальным, но когда читаешь следующим образом: One eontactType classifies zero or one contact (Один тип партнера классифицирует ноль или одного партнера) то это не имеет логического смысла. Вероятно, мы хотели бы исправить эту ситуацию в нищий модели: eontactType contact ..Tty..... J для которой мы теперь читаем: One eontactType classifies zero or more contacts. (Один тип партнеров классифицирует ноль или большее количество партнеров.) Обратите внимание, что тип отношения (идентифицирующее, неидентифицирующее, необязательное, обязательное) не делает никаких различий при чтении отношения.
Мы можем также включать глагольную конструкцию, которая читается от- потомка к предку. Для отношения "один ко многим" это имело бы следующий формат: one child record <relationihip> exactly one parent record. (одна запись-потомок <отиошенив> точно одну запись-предка.) Для нашего первого примера нам следует прибавить дополнительную глагольную конструкцию: Это будет читаться совершенно противоположно предыдущим примерам, т. ел One phon eNumber may be used to phone exactly one contact (Один иомер телефона может использоваться, чтобы позвонить точно одному партнеру.) Так как мы идем от многих к одному, то будем всегда знать, что предок в отношении будет иметь относительное значение, и так как мы читаем в контексте существования потомка, то можем также предполагать, что имеется единственная запись-потомок, рассматриваемая в предложении. Другие методы изображения отношений В этом разделе мы постараемся кратка описать две других методологии моделирования, е которыми вы, вероятно, часто будете иметь дело при проектировании БД для SQL Server 2000, Information Engineering (информационная разработка) Методология IE (Information Engineering) хорошо известна и широко используется. Она действительно весьма популярна и успешно работает при отображении необходимой информации. Эта методология также нежно называется методом "птичьей" лапки. Изменяя основные символы ва конце ливни, мы можем получить все различные варианты отношений. Следующая таблица показывает различные символы, которые мы можем использовать, чтобы строить представлемия отношений: Символ Описание I - <| Многие — сущность на стороне с “птичьей лапкой” означает, что может быть более одного значения, которые относятся к экзем и л яру другого свойства I - ° I Иеобязателькая еупщость — указывает, что не обязательно будет связанный экземпляр сущности с этой стороны отношения для экземпляра сущности на другой стороне, Это может быть огшеаио как "нуль или большее количество" в противоположнеють к описанию "одно или большее количество" - Идентифицирующее отношение — ключ сущности с другой стороны отношения мигрирует в эту сущность Необязательное отношение — пунктирная линия с одной стороны отношения указывает, что мигрирующий ключ может быть NULL
Например: Table A, TaQie В .......... .........................' Один юэ многим, обязательное неиденгифициоу’ощее Taele А = ' ТаЫеВ Один ео многим, обвза'ельное, идентифицирующее Table А ТзЫаВ -----------------et_ Один к одному, необязателытсе, идентифицирующее Table А ТаЫеВ .......... .......,.На,,4~~ Многие ко многим Атрибуты показываются почти таким же способом, как и при использовании IDBF1X, внутри прямоугольника. Это обозначение не столь ясное, как в IDEi’IX, но оно довольно хорошо работает и, вероятно, будет в некоторых документах, с которыми мы встретимся в качестве архитектора данных. Методология IE не всегда в полной мере реализована в каждом программном средстве. Однако круг и "птичьи лапки" обычно реализованы должным образом. : Дальнейшие дета№ зтой методологии могут Выть найдены в "Information Engineering" (Prentice Hall, ISBN 013434462X (vol. 1). 0134648854 (vol.(vol. 3). Chen ERD (диаграмма Чена отношений между сущностямий) Chen ERD отличается очень немногим, но довольно иаглядаа. На следующем рисунке мы представляем очень простую диаграмму с показанными основными элементами диаграммы Чена. m
Каждая сущность снова представляется прямоугольником; однако атрибуты не показываются на диаграмме, а приложены к сущности в виде кружков. Первичный ключ обозначается двойным подчеркиванием. Мощность отношения обозначена в тексте. В примере это — 1 и только 1 Сущность 1 <название отношениям О ко многим Сущность S. Основная причина использования формата Chen ERD — для разнообразия. По крайней мере, одна методология (Бахмана) реализует атрибуты в этом стиле, когда они не показываются прямоугольнике. Хотя я и понимаю логику этого (атрибуты являются отдельными объектами), но пришел к выводу, что модели, которые я видел в этом формате, выглядят чрезмерно загроможденными даже для очень маленьких диаграмм. Однако этот формат хорошо работает с логической моделью вез использования разных "тайных символов", чтобы описать мощность. Хотя я и не могу сказать, что эта методология не используется в средствах проектирования ВД, но я нигде ее не видел, за исключением Microsoft Visio, кроме того, многие из диаграмм, которые вы найдете в Интернете, будут изображены в этом стиле, так что интересно вто понять. Дальнейшие детали могут выть найдены вПе Entity Relationship Model — Toward a Unified View of Data, (ACM Transactions on Database Systems, Volume 1, No. 1 (March 1976). pp 9-86). Средства построения диаграмм Microsoft SQL Server В программном средстве Enterprise Manager пакета SQL Server версии 7.0 было добавлено новое довольно изящное средство — средство изображения диаграмм ВД. Оно также довольно интересно изображает сущности. Следующий рисунок — пример отношения "один ко многим": Parentld Attribute 1 Attributes «а Attributes Parentld niia Первичные ключи идентифицируются небольшим ключиком у атрибута. Отношение обозначается линией между сущностями, на одном конце которой находится ключик, а на другом конце — знак бесконечности. Мы можем показывать сущности в иескольких форматах: так, как только что изображено, или с изображением типов данных всех атрибутов: На атом, рисунке Column Лаве — имя столбца, Data Туре — тип данных, Length — длина информации в байтах, Allan Nulls — разрешение использования значения NULL. На следующем графике я привел модель всей БД Northwind, используя средство изображения диаграмм, чтобы показать, что оно на самом деле хорошо отображает модель:
Однако, хотя это все и правильно, здесь средство моделирования данных показано не полностью и, вероятно, если будет нужно, не Судет использоваться таким образок. Мы включаем методологию моделирование SQL Server потому, что ояа включена в SQL Server, и понятно, что в некоторых ситуациях это — лучшее средство, которое вы можете использовать. Эта методология — только физический инструмент моделирования, и позволяет использовать все конкретные возможности реализации в SQL Server, включая возможность аннотировать наши таблицы и столбцы описательной информацией. Она является мощным средством для распределения конкретно! структуры между разработчиками ВД. Так как это довольно специфическая реализация, то если мы решим осуществить отношение с помощью триггера, то в структуре не будет видао, что этот триггер существует. В большинстве случаев это не лучшее средство представления информации конкретного отношения, разработанного для ВД, ио, если необходимо, дает полезный взгляд на структуру БД. Описательная информация Мы выделили наши сущности, назначили атрибуты и домены к ним и установили отношевия между ними, но сделали не все. Мы обсудили обозначение наших сущностей, атрибутов и даже наших отношений, но даже с правильно построенными названиями все еще, вероятно, будет неясность относительно того, для чего используется каждый атрибут.
Нужно добавить комментарии к картинкам в нашей модели. Комментарии позволят потенциальному читателю, и даже вам самим знать, что вы первоначально имели в виду. Помните, что не каждый, кто будет рассматривать модели, которые мы создаем, Судет иа том же техническом уровне, на котором находятся нереляционные программисты, не говоря уж, конечно, о пользователях или (не технических) менеджерах продукта, которые не имеют никакого опыта моделирования, Мы уже сделали хорошее начало этого процесса в предыдущей главе,- где добавляли эту информацию в наш документ, созданный в редакторе Word. Заметим, что как только мы найдем средство моделирования данных, которое будет нам удобно, мы должны иметь возможность обращаться со всеми данными, которые мы вводили в предыдущей главе, и даже больше. Это может оказаться намного быстрее, чем использование документа "Word для размещения всей информации и затем передачи ее в модель данных, Для описательной информации не нужен специальный формат; она просто должна быть детализированной, современной и способной отаетнть на многие вопросы, которые мы, вероятно, будем задавать. Каждый из этих элементов информации должен выть размещен таким образом, чтобы пользователям было легко связать его с той частью модели, где он используется, и должен быть помещен или в документ, или в средство моделирования в виде метаданных, □ Вопросы могут быть следующего типа; □ Что из себя представляет объект? □ Как он будет использоваться? □ Кто может его использовать? □ Какие дальнейшие планы использования объекта? Возможности описаний не должны простираться за пределы объектов или сущностей, о которых идет речь. Например, описание сущности должно относиться только к сущности, а не каким-либо связанным сущностям, отношениям или даже атрибутам, если это не является необходимым. Определения атрибута должны говорить только о конкретном атрибуте и о том. каковы могут быть его значения. Поддержание хорошей описательной информации эквивалентно помещению приличных комментариев в кода. Поскольку ВД — всегда центральная часть любой компьютерной системы, комментарии на этом уровне более важны, чем в любом другом месте. Например, пусть мы имеем следующие две сущности: eontactType eontactType!d name description ишееифицячет contact comactid -t —---—-—— eontactType id (FK) name Здесь eontactType — сущность "им партнера”; contact: — сущность "партнер"; contactTypeld — идентификатор типа партнера; contacted — идентификатор партнера; пгше — имя; description — описание.
Мы могли вы иметь следующий довольно общий набор описательной информации, размещенной. чтобы охарактеризовать атрибуты, которые мы создали: Сущности Contact Человек, с которым устанавливаются связи в деловых целях Attributes Описание Contactld Первитаый ключ, указатель на партнера (contact) Con tactTypcId Первичный ключ, указатель на тип партнера (contactType) Name По л и ое имя- партнера (contact) ContactType Домены различных типов партнеров (contact) Attributes Описание ContactTypcId Первичный ключ, указатель яа тип партнера (contactType) Nome Уникальное имя типа партнера Description Описание, как именно можно использовать партнера Отношения: Имя СУЩНОСТИ' предка Фраза Имя сущности- потомка Определение ContactType классифицирует Contact К л а ссификация типов партнеров. Потребуется при спецификации. Учебный пример В нашей предыдущей главе taa создали документ, содержащий все объекты, которые яви нужно смоделировать. В «токе просто хотим преобразовать всю информацию, которую помещаем в документ, и поместить ев в набор графических моделей, где это имеет смысл. Модель действий Начнем с модели действий, потому что это вообще первая модель, которую вы должны создать. В нашем учебном примере интервьюер определил двух исполнителей: основного пользователя счета и клерка бухгалтерского учета. В то время как пользователь счета
выполняет действия типа получения информации о счете, взятия денег, вложения депозитов и так далее, клерк бухгалтерского учета должен выполнять задачи типа органнаадии доступа к информации о счете и ввода сделок. Мы можем суммировать исполнителей и действия, которые для нас будут представлять интерес, когда начнем организовывать защиту и писать процедуры, следующим обрезом: Исполнители: Название Ошевше Основной пользователь счета Каждый пользователь, который может помещать или брать деньги со счета Клерк бухгалтерского учета Пользователь, который может вводить и поддержизать счета Действия: Название Описание Получение информации о счете Позволяет пользоватешо получать информацию о счете, такую как название банка, баланс и т. д. Ввод сделок Позволяет пользователе ляпиемпиячч. произведенные сделки Ввод чека Позволяет пользователю оформить чек сделки Баланс Позволяет пользователю поддержать баланс прихода и расхода, представляемый банком Оператор загрузки Позволяет пользователю загрузить информацию оо операциях, совершенных банком Изъятие денег Пользователи получают деньги со счета для расходования Организация депозита Пользователь помещает деньга на счет Выписка чека Пользователь пишет бумажный документ и дает его другому человеку для передачи фондов
При разработке реальной системы модель действий — очень важная часть проекта. Однако так как наш пример фактически довольно прост, мы не будем слишком тщательно разрабатывать совершенную кодов, действий, Следующая диаграмма действий — возможный результат анализа, который мы выполнили: Хотя и далекая от анализа полного процесса, эта модель дает хороший набор действий и исполнителей для нашей небольшой системы текущих счетов, Обратите внимаете, что в примере мы вставили <<использует>> я «расширяет», чтобы сделать его более полным. Баланс использует загрузку сделок (если то возможно), чтобы получить сделки из электронного источника, а ввод чека «расширяет» ввод сделки, потому что чек содержит я значительной степени ту же самую информацию, что и общая сделка, с некоторыми дополнениями. Мы не включили депозиты или прямое изъятие как отдельные действия, чтобы сэкономить место.
Это — то, как мы собираемся использовать действия. То, -что мы предетавнли здесь — просто задачка, предназначенная специально помочь вам, как архитекторам данных, построить модель, которая овмсияет, каковы процессы в нашей системе, Дальнейшие детали могут выть найдены в подробном изложении (полный документ находится в http://www.rationHl.Com/uml) или читая Пьера-Алана Мюллера “Instant UML" (Wrox Press, ISBN 1861000871). Тогда вы можете получить один из нескольких доступных UML-инструментов и начать использовать действия и UML. UML, вероятно, будет использоваться для полного и исчерпывающего понимания и позволит легче , взаимодействовать с нереляционными аналитиками, с которыми мы должны иметь дело. Модель данных Завершив распознавание процессов, мы переходим к основному этапу формирования модели данных — выбору нашей предварительной структуры ВД, Первый шаг в моделировании данных — модели сущностей и атрибутов, которые предварительно были выделены. Замечание: В остальной части примера мы обычно предполагаем, что любые определения, которые мы ранее добавили, содержатся в лс-бели. Мы не можем расширить все паля, чтобы показать определения, но будем, однако, пробовать дать определения для всех новых объектов, которые мы создаем. Таким образом, мы просто создаем сущности для каждого из наших объектов, добавляя все атрибуты, выбирая базовые типы доменов следующим образом: Account Statement Number: Sting Balance Dale: Datetime Punning Total: Numoe® Name: String Transacted DlrectAthdrawa Type: String i= Orb-nossBaran-se: Np-ber MviousBaianceData: Datetime CumeniSaiarice: Number Slstemsnfflale: Date time otaiDebits: Number TatalCredits: Number ЙШпапоИтг. Number Date: Datelime Number -Sir,ng Descriptor: String Amount: Number Type; String Wihdra walDa-e. Datelrme Number; Number Amount Money CneckReg.ster eegeterType. dunkmjwnh Check Deposit Payee UsageTyoe String Nambe®: Siring tMMMtlW Data'me Amour® Money Memo String . : Signature: Blob Dats: Datetime Number: Number DesertStrom Str ng Amount: Maney Name: Number «dressrAddrsss P hone Num ber: Phone Number Здесь мы используем обозначения, введенные ранее (см,, например, главу 4).
Теперь мы имеем все сущности из примера предыдущей главы в гораздо более компактном и легком для сопровождения формате. Следующий тяг — идентифицировать все отношения, где два объекта по существу одно и то же, и реализовать их как подтипы. Мы имеем один такой вариант в нашем примере: сущности Check (чек), Deposit (депозит) и DirectWlthdrawal (прямое изъятие) — все являются только типами сделок (Transaction). Обратите внимание, что мы добавили атрибут типа дискриминатора для подтипов и удалили все атрибуты, которые являются дубликатами тех, которые уже были в сущности Transaction./
Следующим шагом является добавление и задание имен всем отношениям в модели. Мы все еще не добавили первичные ключи к модели, так что на этой стадии не имеем никакой миграции ключей. Это позволяет проще начать реализацию. Фактически мы добавим ключи на следующем шаге. Заметьте, что мы рассматриваем отношение "получатель платежа — чек" как неидентифицирующее отношение. Это связано с тем, что чек не определяется своим получателем платежа, так как получатель платежа — просто атрибут чека. Но получатель, однако, требуется. Заключительный шаг в создании нашей начальной модели должен идентифицировать все естественные уникальные ключи в данных. Например, в сущности account (счета) число, вероятно, будет уникальным значением. В сущности bank (банк) мы имеем название банка, которое должно выть уникально. Transaction» (сделки) будут, вероятно, иметь для идентификации уникальные номера. Наши предположения будут иногда правильными, а иногда — не совсем правильными, но они — логические предположения. Как всегда, в любое время вы предполагаете, что должны это проверить, и ваша модель и сопроводительная документация должны всегда иметь признак выполнения проверки.
Например, я сущности transaction (сделка) мы установили атрибут number (номер) как уникальный. На первый взгляд ато имеет логический смысл, но нескольку мы продолжаем работать, то (будем надеяться) поймем, что номер сделки уникален только для данного счета. Существует несколько неправильных способов обойти я*о, например, добавлением номера счета к началу номера каждой сделки (это то, что известно как интеллектуальный, разумный или составной ключ, но его может оказаться трудно быстро расшифровать, поскольку он не выделяется в модели ляниыф. Правильным решением будет добавление нервичвого ключа сущности account (счет) к вторичному ключу, который мы сформируем. Это будет означать, что наша модель теперь выглядит следующим образом: Bank Statement
В этой модели мыгобычно будем использовать в качестве первичного ключа целые числа, причем они не должны быть редактируемые. Это также делает здесь выбор легче. Выбор первичного ключа — в значительной степени вопрос вкуса, т в этой книге мы акцентировали внимание на первичных ключах, ориентированных на указатели. Мы разъясним, почему следует физически реализовывать ключи таким образом, в главе 10, Однако, как мы сказали ранее, не выбрав надлежащи» первичных-;-ключей к этому моменту, жы не завершим работу с разрозненной моделью^ получаемой от миграции ключей. Checkflegisterid 'BankdldiPK; Aocouma (fk) Transacted Id (FKI □ate: (tatami) DescriaSon Amount 4И* ВедйегТуре Payeaid Address - PlteeHui liter DtrecxWitndra wai Deposit__________ f Bankdld (FKJ I AocowiWfFK) I Transacted Id (FK; ----------- fBansdid |FK) Account'FK; Transacted!;; iFX) UsageType Memo .^Signature_____ деньги, пере^нныес помощью
Заключительный шаг, который мы должны сделать, — выбрать логические домены для каждого из атрибутов. Идея здесь заключается в том, чтобы попробовать сгруппировать подобные атрибуты в один и тот же домен, как, например, в случае атрибута date (дата) в сущности transaction (сделка),preuiousBalanccDati (дата предыдущего баланса) в сущности statement (отчет) и halaneeDate (дата баланса) в сущности account (счет). Для атрибутов первичного ключа мы зададим домен первичного ключа, и пока оставим их такими. Наша модель теперь выглядит следующим образом: Будем дальше назначать домены каждому из атрибутов, которые создадим, продолжая стадию логического моделирования. В данный момент мы определили и использовали следующие домены: Address (адрес) Включает все адреса AlphaNumericNumber (алфавитно-цифровой номер) Величина, на которую обычно ссылаются как на число, но фактически может содержать алфавитные и цифровые данные Amount (сумма) Денежная величина Balancingttems (элементы баланса) Группа элементов, необходимых для Ллавеа счета ПроЛолжекие таблицы на следующей странице
Blob Рисунки, документы в двоичном формате (BLOB — Binary targe Obj ect) Code (код) Величина, используемая как вид текстового указателя Date (дата) Значение даты Name (имя) Удобочитаемый признак, который назначается экземпляру сущности для легкого доступа РhoneN umber (номер телефона) Полный номер телефона PrimaryKey (первичный ключ) Используется как указатель к экземпляру сущности На данном этапе мы яв будем делать большего в терминах назначения предикатов этим доменам, так как сделаем несколько изменений в модели в ряде следующих глав, и несомненно обнаружим большее количество доменов (а несколько удалим). Мы, однако, не будем отставать с процессом добавления доменов всякий раз, когда создаем новый атрибут, поскольку наша модель становится все более зрелой. Как вы можете видеть, модель становится немного больше, а также начинает проявлять признаки реализуемой структуры. Мы будем делать много изменений в структуре в последующих главах, но заключительная структура будет все еще несколько напоминать то, что шл имеем сейчас. На данном этане мы будем использовать определения наших атрибутов, которые собрали ранее. В качестве примера возьмем сущность statement (отчет): Statement (отчет) Представляет документ (бумажный или электронный), который поступает из банка раз в календарный месяц, и который сообщает нам все, что думает банк о наших тратах Атрибуты Описание ВапкИ (код банка) Идентификатор первичного ключа баяна Statementld (код отчета) Илентификятор первичного ключа э кземп ляра отчета Туре (тип) Определяет тип отчета, полученного из банка PreviousBalance (предыдущий баланс) Определяет, какой предполагался баланс после последнего балансового отчета PreviousBalanceDate (дата предыдущего Валаиеа) Определяет дату составления последнего баланса по счету CurrentBalance (текущий баланс) Определяет баланс счета после того, как все элементы отчета были сформированы StatementDate (дата отчета) Определяет дату, когда сформирован отчет TotalCredits (onтпий кредит) Сумма всех элементов, которые добавляют деньги на счет в период, охватываемый отчетом TotalDebits (общий дебет) Сумма вых элементов, которые изымают деньги со счета в период, охватываемый отчетом Balancingltems (элементы баланса) Все элементы, которые выполнил банк, и о которых он теперь оповещает нас
Резюме В этой главе рассмотрен текстовый проект, который получен в предыдущей главе, и помещен в более краткий графический формат. Здесь представлены два конкретных типа моделироваетя, а именно, модели данных UML и IDEF1X. Мы кратко рассмотрели UML, и особенно использование действий, которые, прежде всего, позволяют нам применять диаграммы действий для моделирование процессов, которые мы, вез сомнения, обнаружим на стадии проектирования ВД. При моделировании данных мы прежде всего сосредоточились на методологии моделирования IDEF1X, детально рассмотрев символику, которая нам понадобится в процессе проектирования. Основной набор выделенных символов позволит на-,- в деталях полностью моделировать БД сначала на логической, а затем и на физической стадии проектирования. Последний момент, который мы отметили, это то, что для каждой сущности в каждого атрибута, которые мы выделили во время процесса моделирования, ням нужно, по крайней мере, идентифицировать следующие детальные свойства: Свойство Цель Name (название) Название, которое полностью определяет цель сущности или атрибута Description (описание) Полное контекстное объяснение назначения атрибута для программиста, а также и пользователя Predicate» (Domain) (предикаты (домеи)) Обычно предикаты «ищеделяют ЛЮБЫЕ правила, которые управляют данными при воде или выводе их из иашей БД. Обычно достаточно просто установить домен (единственный предикат столбца, основанный на ограничениях и типах данных на стадии физического моделирования) и задокументировать другие предикаты на уровне ВД или таблицы Мы будем использовать эту описательную информацию позже в процессе построения наших проверок ограничений, построения триггеров и т, д., чтобы защитить и использовать данные как в надежном хранилище. Таким образом, пользователь может обеспечить конфиденциальность того, что они представляют, и гарантировать, что они будут действительно тем, что должно выть размещено в БД, Рассмотрев символику, которая нужна для моделирования нашей БД, мы теперь должны рассмотреть нормализацию нашего проекта, что и сделаем в следующих главах.

Методы нормализации Независимо от того, какую СУБД вы разрабатываете, нормализация — жизненно важный процесс, который должен бить выполнен, чтобы гарантировать, что ВД вашей системы является и точной, и не содержит дублированной информации. Нормализация может быть суммирована в единственном предложении: "Каждая сущность в вашей ВД должна иметь единственную тему", (http.//vAW,'.gslis.utexas.edu/-!384k11w/normstep.html) ' ' Разли чные уровни нормализации ВД указывают, как хорошо структура придерживается признанных стандартов проектирования ВД, хотя после третьего уровня имеются «которые разногласия среди разработчиков ВД относительно того, насколько полезны дальнейшие уровни. Мы рассмотрим эти аргументы в следующей главе. Как вы могли бы предположить, структура БД — одна иа наиболее важных тем для архитектора БД. Разногласия часто возникают между архитекторами БД и разработчиками клиентской части относительно того, как хранить данные. Почему? Потому что никакие два проекта ВД не похожи друг иа друга, и всегда имеется несколько правильных споаЯов структурировать ВД. В этой и следующей главах мы рассмотрим основные принципы, которые помогут вам избежать многих, многих путей, которые приводят к плохим структурам ВД. Базу данных для обработки транзакций в реальном масштабе времени (GLTP), которая не была нормализована, вообще-то создать вначале для клиента быстрее, так как в ней намного меньше таблиц. Однако это преимущество скоро исчезнет, корда потребуются незначительные изменения в работающей системе. Опытны» проектировщики БД понимают, что изменения в структуре данных требуют больших затрат. Когда авт никаких ааямвей в таблице и никакого кода для доступа к таблице, изменения структуры можно выполнить
просто за минуты. Когда мы добавили сотню записей в таблицу, причем семь из них имеют по десять записей в связанных таблицах, а программисты написали сотни строк кода, мы можем легко потратить час полезного времени, изменяя структуру таблицы. Если имеется миллион записей в таблице, забудьте об этом. Как только мы изменили структуру таблицы, нужно также изменить и весь код для доступа к таблице. Нормализация будет также экономить много места для размещения данных, поскольку главная цель состоит в том, чтобы избежать повторения данных. Процесс нормализации также гарантирует последовательные структуры сущностей, которые помогут нам избежать изменения наших существующих сущностей в будущем. Если мы проделали приличную работу на стадии проектирования, связанной со сбором данных, процесс нормализации должен устранить большинство сомнительных областей в ВД. Естественно, я не говорю, что, как только вы создали проект, нельзя изменять его, поскольку расширения и изменения в деловой практике могут вызвать много необходимых поправок. Однако более легко добавить новую информацию и новые сущности в полностью нормализованную БД. Эта и следующая главы посвящены признанным методам нормализации, и вы увидите, что результаты нормализации минимизируют наличие дублированной информации в ваших БД и сделают их более легкими для управления и изменения, а также более безопасными — хотя это создаст намного больше сущностей. Почему нужна нормализация? Есть много причин дан нормализации структур данных. Уменьшение значений NULL Значения NULL могут выть очень вредны для целостности данных и пронзводательноети, они могут приводить к неприятностям при формирования запросов к БД. Однако зиачения NULL интересны сами по себе; много можно говорить о том, как SQL Server 2000 обращается с, ними и кек можно избежать их или минимизировать их воздействие в ваших БД. Но чтобы не отвлекаться здесь, правила обработки NULL вудат рассмотрены более глубоко в главе 12. Однако процесс нормализации может привести к уменьшению количества значений NULL, содержащихся в вашей ВД. Устранение избыточных данных Любая редактируемая часть данных, которая ие является первичным ключом таблицы (или частью ключа), ио является- внешним ключом (или частью его) и появляется более чем один раз в ВД, потенциально может вызвать ошибку. Конечно, мы видели это раньше, если имя человека размещено в двух местах, и одно из них изменяется, а другое — нет, то неожиданно мы имеем два имени там, где прежде было только одно. Проблема с хранением избыточных данных будет очевидна для любого, кто менял свой адрес. Каждая государственная власть требует, чтобы вы лично изменили информацию об адресе в налоговых формах. водительских правах, регистрации автомобиля и т. д., вместо того, чтобы сделать централизованно одно изменение.
Устранение ненужного кодирования Для управления ненормализованными данными может потребоваться допшдаггельное программирование в триггерах, хранимых процедурах, или даже в логических цепочках бизнеса, и это, в свою очередь, может существенно повредить работе. Не говоря уж о том, что дополнительное кодирование увеличивает шана возникновения новых ошибок в лабиринте кода, который требуется, чтобы обслуживать избыточные данные. Много проектов ВД терпят неудачу иа-аа чрезмерного требования синхронно хранить избыточные данные. Максимизация кластерных индексов Кластерные индексы используется для задания исходного порядка таблицы в SQL Server. Это специальные индексы, в которых физическое хранение данных соответствует порядау индексированных полей, который позволяет лучше реализовывать запрос, использующий этот индекс. Как правило, они используются, чтобы организовать таблицу удобным образом для увеличения производительности. Каждая таблица может иметь только один кластерный индекс. Чем больше кластерных индексов в вашей БД, тем меньше сортировки вам, вероятно, придется делать, и более вероятно, что в запросах можно вудет использовать MERGE JOIN (соединение слиянием} — специальный тип очень быстрой операции соединения, которая требует отсортированных данных. Сортировка —: очень дорогостоящее действие, которого нужно избегать, если возможно. Кластерные индексы и индексы вообще будут детально рассмотрены в главах 10 и 14, Уменьшение числа индексов на таблицу Чем меньше индексов на таблицу, тем меньше страниц нужно перелистать при корректировке таблицы или добавлении в нее данных. Под страницами мы понимает не то, что называется ЖеЪ»страницами, а концепцию страниц в SQL Server. В SQL Server данные и индексы разбиты и размещены иа странипах размером в 8К. Конечно, SQL Server не хранит в любой момент всю ВД в памяти. Что ои делает, так эта держит "снимок" того, что используется в настоящее время. Чтобы сохранять иллюзию наличия (всей ВД в памяти, SQL Server вводит й выводит страницы на высокой скорости, чтобы довраться до нужной области памяти, когда это требуется, однако эта область может содержать только ограниченное число страниц в любой момент. Страницы поэтому вводятся в оперативную память и выводятся из иве по принципу, что те, к которым наиболее часто обращаются, остаются в памяти. Операция перемещения страниц является дорогостоящей с точки зрения производительности. Поэтому, чтобы обеспечить максимальную производительность, вам нужно перемещать как можно меньше страниц. Когда таблица имеет много столбцов, вам может понадобиться несколько индексов для таблицы, чтобы избежать проблем с реализацией поиска. В то время как зги индексы могут дать существенное улучшение поиска, само поддержание индексов может быть очень дорогостоящим. Индексы — очень хитрая тема, потому что они обладают и положительным, и отрицательным воздействием на производительность и требуют четкого баланса для £ , оптимального использования. Хранение "тонких'’ таблиц Когда юл называем таблицу "тонкой”, то подразумеваем» что в ней имеется отноеитеда|но небольшое число столбцов. Более "тонкие" таблицы подразумевают, что большее количество данных размещается на данной странице в вашей БД, таким образом позволяя серверу БД восстановить большее количество строк таблицы за одно чтение, чем в противном случае. Все это означает, что в системе будет большее количество таблиц, когда мы закончим
нормализацию. Имеется, однако, предел здравого смысла, и я яа рекмивндую иметь таблицы с одним столбцом. Имейте также в виду, что в типичной OLTP-системе обычно используется немного столвцов при каждом изменении данных, я часто используются запросы. чтобы получить в итоге единственное значение, подобно балансу счета. Для этих видов запросов требуется высокая производительность Процесс нормализации Если мы вспомним одно наше- описание нормализации из первого раздела дамой главы —. "Каждая таблица в вашей ВД должна иметь единственную тему"—то это может подразумевать, что каждая таблица должна стараться представлять единственную сущность. Данная концепция станет совершенно очевидной в концу следующих двух глав, когда мы просмотрим процесс нпрмалпаапии Нормализация — ио процесс. Имеются семь следующих широко используемых типов . нормализации: □ Первая нормальная форма (ШФ) □ Вторая нормальная форма (2НФ) □ Третья нормальная форма (ЗНФ) □ Нормальная форма Войса-Кодда (НФБК) □ Четвертая нормальная форма (4НФ) ’ □ Пятая нормальная форма (5 НФ) □ Нормальная форма домен-ключ (НФДК) В 1972 г. В. Ф. Кодд предеиаил миру Первую нормальную форму (1НФ), основанную на форма данных, и Вторую и Третью нормальные формы (2НФ и ЗНФ), основанные на функциональных зависимостях данных. Они были дали улучшены Коддом и Бойсом в Нормальной форме Бэйса-Кодда (НФВК). Данная глава детально охватывает эти четыре формы. Во время обсуждения нормализации в згой и следующей главах мы пройдем через каждую из указанных выше форм, рассматривая, кек устранить все нарушения, которые мы выявим с помощью соответствующих правил, сформулированных для веждой формы. Мы могли вы решить проигнорировать некоторые из нарушений для целесообразности, и мы рассмотрим это по пути. Важно также не только прочитать о каждая форме в книге, но и рассмотреть каждую форму в контексте логического- моделирования. Когда мы дойдем до конца следующей главы, должно быть явно, что ЗНФ — яе волшебная форма, как полагают некоторые архитекторы ВД. Первая нормальная форма Первая нормяльня я форма (ШФ) связана с формой атрибутов и записей. Она наиболее важная из всех нормальных форм. у Эта форма также используется в определении реляционной модеяя ВД, и определение 1НФ — одно из двенадцати правил Кодаа, которое фактически связано не е нормализацией, а скорее с набором правил, которые определяют реляционную ВД, Эти правила перечислены и обсуждены в приложении А.
Сущности в 1НФ будут иметь следующие характеристики: □ Все атрибуты должны быть элементарными, то есть, иметь только одно единственное значение, представленное в единственном атрибуте единственного экземпляра сущности. □ Все экземпляры я таблице должны содержать одно и то же число значений. □ Все экземпляры в таблице должны быть различны. Нарушения 1НФ проявляются в физических моделях при работе с беспорядочными данными, что мы вкратце рассмотрим. Все атрибуты должны быть элементарными Атрибут может представлять только одао единственное значение, он не может выть группой. Это означает, что не может быть никаких массивов, никаких разграниченных данных, и никаких атрибутов с несколькими значениями, размещенными в поле. Перефразируя это, значения, помещенные в атрибут, не могут быта раздроблены на меньшие части, В качестве примера мы рассмотрим некоторые общие нарушения этого правила 1НФ. Поля адреса электронной почты В сообщениях электронной почты адрес обычно помещается в следующей форме: riamelldomalnl. com;name20domain2. сот; патеЗвйотахг.З. сот, Это явное нарушение 1НФ, так как мы пытаемся разместить более одного адреса в одном поле электронной вочты. Каждый адрес электронной почты должен формировать отдельное поле. Имена Рассмотрим полное имя "Джоя К Паблик". Это — очевидная проблема, поскольку мы имеем имя, инициал отчества и фамилию в единственном поле. Вели разбить полное имя на три части, то получим поля first_name (имя), middlе_initial (инициал отчестве) (или middle_name (отчество), которое я предпочитаю), и last_name (фамилия), Это обычно хорошо, так как полное имя человека в США обычно имеет три части, В некоторых ситуациях это может быть и недостаточно, и вы можете не знай чиеяо частей, пока пользователь не введет данные, И тем не менее вы должны знать требования к данным. Номера телефона Рассмотрим случай номера телефона. Американские номера телефона имеют форму "1-423-555-1212" плюс некоторое возможное число расширения. Ив наших предыдущих примеров вы можете видеть, что имеется несколько полей, вложенных в этот номер телефона, ив говоря уж о возможных расширениях. Кроме того, часто нужно хранить в БД не только американские номера телефона. Решение относительно того, как обращаться с этой ситуацией, может быть полностью основано на том, как часто вы храните международные телефонные номера, поскольку могло бы выть невозможно (могло вы быть — я не пробовал, так что не могу быть уверенным на все сто процентов) создать таблицу или набор сущностей, чтобы управлять любой ситуацией.
Так, для американского стиля номера телефона нам потребовалось бы пять полей для каждой из следующих частей: C-AAA-EEE-NNNN-XXXX О (С) Код страны — это то, что мы навираем для номеров, которые находятся не в пределах кода региона, и обращений к телефону, когда мы набираем не местный- номер, Q (ААА) Код региона — указывает вызов региона, который расположен в пределах штата. □ (ЕЕЕ) Телефонная станция — указывает набор чиселв пределах кода региона □ (NNNN) Номер — число, уникальное для индивидуальных телефонных номеров. □ (ХХХХ) Расширение — число, которое должно быть вызвано однажды, когда вы соединяетесь, используя предыдущие номера. Адреса К данному моменту вы должны знать, что весь адрес должен быть разбит на поля для размещения уличного адреса, города, штата, и почтового кода (здесь и далее мы игнорируем для кратности международный фактор). Однако уличный адрес должен также выть разбит в большинстве случаев иа номер дома, название улицы, нотор квартадый:нсчтовый-ящик. Мы кратко упомянем уличные адреса далее еще раз, 1Р-адреса IP адреса — очень интересный случай, потому что они состоят иа четырех частей данных, формируемых в виде BY1,BY2.BY8,BY4, где BY типа byte (байт). Он представляет четыре различных атрибута, ио фактически является единственным значением целого числа без знака, получаемого по математической формуле (BY1 * 2563) + (BY2 * 256г) + (BY3 * 2561) + + (BY4 * 256°). Следовательно, если вы имеете IP-адрес М.32.1.128, это могло бы быть размещено как 404750720 = (24 « 2563) + (32 * 258s) + (1 * 2561) + (128). Как вы фактически храните это значение, будет зависеть в большой степени от того, что вы будете делать с .данными. Например: □ Если вы имеете дало с IP-адресом как четырьмя различными значениями, вы могли бы хранить та в четырех различных полях, возможно названных ipMdressPart 1 (1-я часть IP-адреса), ipAddreas₽art2 (2-я часть IP-адреса), ipAddrassFart3 (3-я часть IP-адреса) и ipAddress?aro4 (4-я часть 1Р-а^рвса>. Обратите внимание, что, так как здесь всегда будет ровно четыре части, этот тип хранения не нарушает правила ШФ, что все экземпляры сущности должны содержать одно и то же число значений. Это рассматривается в следующем разделе. □ Одна из причин хранить IP-адрес как единственную величину — проверка диапазона. Если вы имеете один IP-адрес и должны выяснить, когда ок находатся между двумя другими IP-адресами, хранение адреса как целого числа позволяет вам просто искать данные с помощью оператора типа: "where cpAddressCheck is between iyAddressLow and ipAddressHigh", где cpAddressCheck — проверяемый IP-адрес, IpAddreesLw — нижняя граница 1Р-адр«а, ipAddressBigh — верхняя граница IP-адреса. Перейдем теперь ко второму правилу 1НФ.
Экземпляры сущности должны содержать одно и то же число значений Лучше это проиллюстрировать на небольшом примере. Если у вас есть сущность, которая хранит иия человека, то, если одна строка содержит одну составляющую имени, то и все строки должны иметь только одну составляющую. Если они могут иметь две составляющие, то все экземпляры должны иметь возможность хранить две составляющие. Вели же они могут иметь различное число составляющих имени, мы должны с ними работать по-разному. Пример нарушения этого правила 1НФ можно найти в сущностях, которые имеют несколько полей с одним и тем же основным именем и суффиксом (или префиксом), представляющим собой число, тииа add га s s_l i пе__1 (адресная шина 1), - address_line^2 (адресная шина 2) и т. д. Обычно это — попытка допустить многократные значения для единственного поля сущности. В редких случаях, когда используется всегда одно и то же число значений, формально нет никакого нарушения 1НФ. Но даже в таких случаях это обычно все же не лучшее решение проекта, поскольку пользователи часто могут измеинть их назначение. Чтобы преодолеть ке ото, мы могли бы создать сущность-потомок, чтобы раяместить плохо сформированные значения исходной сущности, Это также позволит нам иметь фактически неограниченное число значений там, где предыдущее решение имело конечное (и небольшое) число возможных значений. Одна из проблем, с которой вы при этом столкнетесь, это то, что записи-потомки, которые вы создаете, будут требовать упорядочивающей информации, которая обеспечивает соответствующую организацию для использования фактических строк. Обратите ввивание, что фактическая реализация адресов будет, конечно, основана на требованиях создаваемой системы. Мы можем использовать правила мощности, как описано в предыдущей гладе, чтобы ограничить число возможных значений. Если мы должны ввести ограничение,-петому что наша модель утверждает, что нам нужно максимум два потомка и минимум один, мощность обеспечит механизм для этого. ‘ Все экземпляры записей сущности должны быть различны Это кажется очевидным, но об этом все-таки следует сказать. В основном, это указывает, что каждая сущность в 1НФ должна иметь первичный (ила уникальный) ключ, Имейте в «иду, однако, что только добавление искусственного ключа к сущности может фактически реализовать предписание этого правила, ко, конечно, это не самоцель. В следующей главе мы расширим эту идею. Программирование аномалий, которых следует избегать в 1НФ Нарушения 1НФ очевидны и часто очень неудобны, если к столбцам, на которые они воздействуют, часто обращаются. Следующие примеры описывают некоторые из ситуаций, которых мы можем избежать, помещая наши сущности в 1НФ. Коррекция списков в отдельном поле Большая проблема, связанная с нарушениями 1НФ, оврвдаляетвя тем, что ни реляционная теория, ни SQL не могут работать с неэ ле мент ар ними нолями. Рассмотрим кеш предыдущий пример атрибута адресов электронной почты. Предположим, что юл имеем таблицу, названную person (человек) со следующей схемой;
Н CREATE TABLE person - I personld ini NOT NULL IDENTITY, В . name varcharflOOJ MOT ЖИЛ, В e-m&ilAddress vsrcr-йг (10G0I hot KULL В > Здесь создается таблица person (человек), содержащая три атрибута: регвапМ — идентификатор записи, целого тапа, не мажет иметь значение NOLL, является первичным ключам; пате — имя человека, типа троки до 100 символов, не может иметь значение tlULL; e-mail Address — адрес электронной почты, типа строки до 1000 символов, не может иметь значение NULL. Если мы позволим нашим польвовмелям и меть больше одного адреса электронной почты и хранить это в ирийуте адреса электронной почты» ваше поле электронной почты могло вы выглядеть так: "Davidsonsed.сопи Idavidsonee-mail.сом”. Рассмотри» также, что многие различные пользюдегеяк БД могут использовать адрес электронной почты . Davidsonsgd.com. Вели нам нужно изменить адрес электронной почты eDavidsonstd. com на Davidsonsedomain.coa, нам вы потребовался код, подобный следующему, для каждого человека, который использует адрес этой электронной почты: I UPDATE person SET e-raaleAddress •> replace (e-maleAddresses, 'Davidsoosed.com', ' DavidaonsedoiBain.com' I WHERE e-mailAddress like '%Davidseneed.com%' Этот код не выглядит неприятным, ио что сказать относительно случая, когда имеется также н адрес электронной почты theDavidsonsSd. сои? Что, если мы не хотим изменять это значение? Так как код, который вы пишете, должен работать вовсе* случаях, это выло бы недопустимо, потому что предшествующий оператор UPDATE будет иметь нежелательные побочные аффекты в таких случаях. Коррекция полей с несколькими составляющими Логика программирования, которая требуется для изменения части поля с несколькими составляющими, может бытьочень запутанной. Возьмем, например, случай телефонных кодон регионов. В Соединенных Штатах мы имеем большее количество телефонов, пейджеров, телефонных ячеек и т. д., чем то, о котором создатели системы кода региона могда-либо думали, и поэтому они часто изменяют их или вводят новые коды регион он. Вели все значения номера, телефона размещены в элементарных контейнерах, овиоаяеяив кода региона потребовало вы единственный, легко реализуемый короткий SQL-оператор, наподобие следующего: I UPDATE phoneNtJBiber SET areaCade >= '423' WHERE areaCode =» '615' AMD exchange IN ('232' ,'323' , '989'1 В противком случае мы получим следующее: i UPDATE phoneNumber - - ’ ~ SET phonoSumiaer = REPLACE:-none Wurrber, ’-615-', ’-42 } WHERE phoneNumber LIKE • -615- AND substringIphoneWumber, 7, 3} IH ('232','323' , '9894 Заметьте, что substring() — расширение в T-SQL и не входит в стандарт SQL,
Этот пример требует для- правильной работы искусного форматирования телефонных данных, а это редко бывает без увеличенного количества кода. Все это при использовании простого формата удовлетворило бы всем проблемам. Коррекция записей с переменным числом фактов Одна из главных проблем, связанная е разрешением переменного числа ’фактов в данной записи, связан* с различными ситуациями, которые воаиякнут, когда вы будете работать с одним из полей вместо другого. Пусть мы имеем хорошо структурированную таблицу типа: И CREATE TABLE payrects ( payment-aid int ВОТ NOLL IDENTITY. a-count Id int NOT NULL, г payment! money MOT NULL, pawnentZ money NULL ЗЙвеь создается таблица с именем payments (оплаты), содержащая четыре атрибута; paymentlei — идентификатор записи в таблице, целого типа, не может иметь значение NULL и является первичным ключом; accountlci — идентификатор Счета, с которым связана запись, также целого типа, не может иметь значение NULL: payment! — величина оплаты (основная), денежного типа, не может иметь значение NULL; payment 2 — величина оплаты (дополнительная), денежного типа, может иметь значение NULL. Когда пользователь должен делать два платежа (по каким-либо причинам), следующий отрывок кода показывает, что потребуется, чтобы ввести оплату: . I UPDATE payments .. : SET payment! « case WHEN.payment! IS NULL THEN 1G0C.W ELSE payment1 END, payment^ - case ИНВЫ payment! IS NOT NULL AND payments IS NULL THEN 1000.QO ELSE paymentZ END WHERE accountld 1 Конечно, вряд ли точно так будет решаться вадача, но даже если.ат* логика выполнена на стероне клиента, формируя различные блоки произведенных или предполагаемых платежей, все-таки ясно, что такое использование является проблематичным. Альтернативой могла бы быть таблица следующего вида: I CREATE TABLE, payment paymentld int NOT NULL IDENTITY accountId int NOT NULL, — внешний ключ к таблице account date datetime HOT HULL, . . amount гопе у Здесь создается таблица с именем payment (оплата), содержащий четыре атрибута: payment 14 —идентификатор записи в таблице, цело» типа, не может иметь значение NULL и шляется первичным ключом; accounild — ийеяшуфмкатпр счета, с которым связана запись, также целого типа, не может иметь значение NULL; date — дата, оплаты, не может иметь значение NULL; amount — величина оплаты, денежного типа, не может иметь значение NULL.
Тогда добавление платеж* выло бы просто добавлением новой записи в таблицу .payment: IIKSBHT payment (accountld, date, value} VALUES (1, 'June 12 2000', $300.00} Это — конечно лучшее решение. Одна из главных проблем с этим примером — это то, что дата ие является элементарным типом данных (другими словами, она сама — поле с несколькими величинами). Фактически, SQL Server просто имеет тип данных datetime (дата-время), который действительно представляет несколько полей, свернутых в одно поле: день, месяц, год, час, минута, секунды, миллисекунды, и т. д. Тип данных datetime — одна иа раздражающих особенностей SQL Server, который мы часто должны использовав во причинам удобства, но так как он хранит несколько значений, то фактически не соответствует 1НФ. Имеется масса функций в SQT. Server, разработанных, чтобы восполнить факт, что datetime (и smalldatetime) — не реляционные типы данных БД. Есть ситуации, где бдагорааунно реализовать наши собственные типы даты и времени, определяемые пользователем, и работать с переменными времени в разумной манере. Признаки, что существующие данные не удовлетворяют 1НФ Далее мы собираемся посмотреть, как можно распознать, являются ли данные в вашей ВД соответствующими 1НФ или нет. Данные, которые содержат неалфавитные символы Под неалфавитными символами ин подразумеваем запятые,-скобки, круглые скобки, символы конвейеризации и т. д. Они являются предупреждением, что мы имеем дело с полем, содержащим несколько величин. Однако будьте осторожны и не зарывайтесь. Например, если мы решили иметь блок текста, мы, возможно, все хорошо нормализовали, если имеем сущность word (слово), сущность sentence (предложение) и сущность paragraph (абзац). Этот признак белее применим к сущностям, которые содержат списки с разделителями. Имена полей с числами в конце Как мы заметили, очевидным примером этого было бы наличие сущностей с именами - атрибутов ehlldl (первый потомок), child 2 (второй потомок) и т. д. иди, что я предпочитаю, User-Defined! (первый определенный пользователем атрибут), UserDefined2 (второй определенный пользователем атрибут) и т. д. Очень вероятно, что ято неправильно. Это = довольно общий пережиток со времен ВД с однородным файлом. Доступ к данным в нескольких таблицах был дорогостоящим, так что много полей помещалось в единственную таблицу. Но для реляционных СУБД это очень вредно, Вторая нормальная форма Следующие три нормальные формы, которые мы рассмотрим, связаны с отношениями между атрибутами в сущности и ключом в той же сущности, и они минимизируют неподходящие функдаональшае зависимости. Мы увидим, что некоторые виды отношений между ключом и другими полями нежелательны и должны быть устранены. Как выло отмечено в главе 3, функциональная зависимость подразумевает, что если мы используем функцию е одним значением (назовем его значением 1) и результат этой функции всегда точно то же самое значение (назовем его значением 2), тогда значение 2 функционально зависит от значения 1.
Например, рассмотрим следующую ситуацию.- Мы имеем три параметра: имя владельца, тип изделия и серийный номер. Серийные номера подразумевают конкретный тип изделия, таи что они функционально зависимы. Если мы изменим тип изделия, но же будем изменять серийный номер.то наш еерийиыи номер и тип изделия больше на будут соответствовать друг другу, и три рассматриваемых параметра больше не будут обозначать что-то одно. Пользователь не будет пожимать, какое иадеяие имеет в виду владелец, — обозначенное серийным номером или типом- изделия. Следующее высказывание поможет вам понять, чем занимаются Вторая нормальная форма (2НФ), Третья нормальная форма (ВНФ), и Нормальная форма Бойса-Кодда (НФБК). Неключевые поля должны конкретизировать ключ записи, весь ключ, и ничего кроме ключа. Это означает, что исключение поля должны далее описывать ключ записи и не описывать никакие другие атрибуты, Начнем ео Второй нормальной формы (2НФ), которая является, по-вндамому> самой простой из этих нормальных форм. Сущность, соответствующая 2НФ, должна иметь следующие характеристики: О Сущность должна соответствовать 1НФ. □ Каждый атрибут должен описывать весь ключ. 2НФ имеет смысл только тогда, когда сущность имеет составной ключ (ключ, состоящий из двух или большего числа столбцов). Сущность должна соответствовать 1НФ Очень важно выполнить все таге процесса нормализации, чтобы исключить проблемы в ваших данных. Будет трудно выделить проблемы 2НФ, если у вас все еще есть проблемы с 1НФ. Каждый неключевой атрибут должен описывать весь ключ Эго означает, что каждый неключевой атрибут должен описывать сущность, определяемую всеми атрибутами ключа, а не просто их частью. Если это не так, и любой и» неключевых атрибутов функционально зависит от подмножества атрибутов ключа, то вы имеете шанс получить неприятности при корректировке данных. Например, рассмотрим следующую структуру: Ьдс<щвю> «umgrjeecnd-rww Здесь bookauthor — сущность "автор книги"; author_saeiel_eecaricy_ntaiber — номер социального страхования автора, book_ISBN_number '—> ISBN-номер книги, royalty_j>ersentaffe — процент оплаты, book_td.tle — название книги, author_fij:st_nante — имя автора, author_second_nanie — фамилия автора.
Атрибут book_ISBH_nmnber однозначно определяет книгу, а author_soaial_security_number однозначно определяет автора. Следовательно, эти два столбца создают один ключ, который однозначно определяет автора конкретной квиги,- Проблема е другими атрибутами. Атрибут гсу = It y_perceritege определяет платеж, который автор получает да книгу, так что ом связан со всем ключом. Атрибут book_title описывает книгу, но не описывает автора вообще. То же самое происходит с полями author_f irst_name и author_second_name. Они описывают автора, но никак не книгу. Это — яркий пример функциональной зависимости. Для каждого значения, которое имеется в столбце faook_ISBN_nueber, вы должны иметь одно и то же название книги и автора. Но для каждого book_ISBH_nutnber- вы ня обязаны иметь одно и то же значение royalty_percentage — оно фактически зависит и от автора, и от книжного договора, а не только от одного или другого. Следовательно, у нас есть проблемы, и мы должны создать три отдельных супщости, чтобы хранить эти данные: Здесь book_aut:hor — сущность "книга-автор", book —• сущность "книга”, author — сущность "автор”. После того, как мы разобьем сущности, видно, что атрибут royalty_percentage все еще описывает автора, написавшего данную книгу,- basket Lcba теперь ониеывает сущность, определяемую ключом baok_ISBN_nuaber, а атрибуты, связанные с именем автора, описывают сущность author, определяемую ключом author_social_security_number. Обратите внимание, что отношение book — book_author является идентифицирующим ' отношением. Нарушения 2НФ часто логически моделируются идентифицирующими отношениями, где первичный ключ новой сущности мигрирует к сущности, в которой возникла первоначальная проблема. Наша предыдущая иллюстрация демонстрирует ату концепцию весьма хорошо, В исправленном примере мы изолировали функциональные зависимости так, что атрибуты, которые функционально зависят от другого атрибута, фактически функционально зависят от ключа. Так как здесь нет никаких столбцов, которые функционально зависят от части ключа, эти сущности соответствуют 2НФ.
Проблемы программирования, которых можно избежать с помощью 2НФ Все проблемы программирования, которые возникают с 2НФ, а также С ЗНФ и НФВК, связаиы с функциональными зависимостями. Проблема программирования весьма проста. Если вы выполните оператор, подобный приведенному ниже, над нашей таблицей из примера; I UPDATE bcckautuor SET boofc_title “ ''Database Design' WHERE boo« J.'BN :.,:, ,: = '923490328948039’ и измените более одной строки, то могут возникнуть проблемы с вашими структурами. Основная проблема здесь в том, что многие программисты не думают о названиях своих проектов БД, когда производят в большом количестве приложения, так что мы создадим таблицы с экранами клиента, подобно атому: Здесь е дополнение к предыдущим обозначениям Author Information — информация off авторе. Book Information — информация о книге. Рассмотрим, что произойдет, если мы используем этот экран, чтобы изменить название книги с несколькими авторами в ВД, которая имеет таблицу book_author, наподобие показанной на первом рисунке предыдущего раздела "Каждый неключевой атрибут должен описывать весь ключ". Если книга имеет двух авторов, то будут две строки для этой книги. Тенерь. пользователь открывает экран редактирования и изменяет название книги, как показано здесь. Когда ей сохранит изменение, это изменит строку таблицы book^author только для Фреда Смита, но не строку таблицы book_aabhor для его соавтора, Две строка таблицы book_authcr-, - первоначально для одной и той же книги, теперь показывают различные названия. Эта проблема устраняется, используя 2НФ, как показано на втором рисунке том же разделе. В этой форме элемент таблицы book будет соединен с двумя строками таблицы book_author. Изменение заголовка в этом экране редактора изменит па» book_tlt<« в этой единственной строке таблицы book; даа экземпляра строк таблицы book_author связаны с таблицей book только полем book_ISBN_number, так что ВД будет все еще показывать для обоих авторов, что они являются соавторами той же самой таити. Все остается синхронным.
Признаки, что ваши сущности не удовлетворяют 2НФ Признаки для обнаружения, находятся ли ваши сущности в 2НФ, не сто» явны, как признаки для 1НФ. Они дают некоторое предостережение и требуют полной эксперт изы ваших структур. Повторение префиксов имен ключевых полей Эта ситуация — один из коварных подвохов. Рассмотрим повторно наш предыдущий пример: taoMuthor Г""™—“Т“-------“---------!— I author^social_secwity_number I 6ooS,J5BW_num&er гоуьф_регсе;;гэсе Пмй_йве. author Jirat.па me author_second_name Здесь мы имеем author_first_name и author_aecond_naiBe, которые функционально зависят от author_social_security_number. Мы также имеем book_title и book_ISBN_number, которые находятся в той же самой ситуации. Наличие таких очевидных префиксов бывает не всегда, но следует их поискать, поскольку это довольно обычная ошибка, которую делают проектировщики-новички. Повторение групп данных Более трудно распознать повторяющиеся группы данных. Предположим, «по над таблицей, которая упорядочена по каждому из существенных столбцов, многократно выполняются операторы SELECT, каждая pas восстанавливая все строки (если возможно). Если имеется функционально зависимый атрибут от одного из других атрибутов, то везде, где поле будет равно X, мы будем видеть зависимое поле, равное Y. Рассмотрим некоторые примеры входов следующей таблицы. author_social_ sectirity_nwaber boo к_I SBN_r.'jmbe r royaity_persen,tage DELA-777-888 1-861-000-156-7 2 DELA-777-888 1-861-000-338-7 3 GIBB-423-4421 1-861-000-156-7 3 book-title autrier_£iMt_n»a aLithor_seeond_nan:a Instant Tiddlywinks Vervain Delaware Beginning Ludo Vervain Delaware Instant Tiddlywinks Gordon Gibbon 1в2
Booic_title, конечно, зависит от book_ISBN_Number, так что когда мм видим ISBN-номер — 1-861-000-156*7, то можем выть уверены, что название книги — "Instant Tiddlywinks", Если это не так, в ВД что-то не в порядке. Составные ключи без внешнего ключа Если в ключе имеется больше чем один атрибут, который не является внешним ключом, любые атрибуты, которые описывают эти атрибуты, вероятно, будут нарушать 2НФ. Кодирование при наличии этой проблемы Разбор кода ВД — один из хороших способов обнаружения проблем, основанных на технических ресурсах системы, которую жы анализируем. Часто программист будет просто писать код для обеспечения, что нарушение 2НФ не повредит данные, вместо перемоделирования их в надлежащую структуру. Какое-то время в истории реляционных СУБД это, возможно, было единственным способом разобраться с даияов ситуацией; однако теперь, когда технология догнала реляционную теорию, такой подход встречается гораздо реже. Важно понять, что я не пытаюсь заявить, что теория несколько изменилась благодаря технологии. Фактически, реляционная теория была очень устойчивой в течение ряда лет с небольшими изменениями в последаее десятилетие. Десять лет назад мы имели весьма мало проблем, связанных с нормализованной системой, в связи с ограничениями аппаратных средств ЭВМ и ОС, так что углы в наших моделях были сглажены по причинам "реализации". Используя современные аппаратные средства, нет никакой потребности даже начинать сглаживать углы нормализации для реализации. Лучше решить эта проблемы с помощью SQL-соединений вместо спагетти-кода, используемого для обслуживания ненормализованных данных. Конечно, на этой стадии процесса проектирования лучше даже не рассматривать основы реализации, функционирования или любых других моментов, не связанных непосредетвеиио с вопросами соответствующего логического размещения данных. Третья нормальная форма Сущность, которая соответствует Третьей нормальной форме (ЗИФ), будет обладать следующими характеристиками: □ Сущность должна соответствовать 2НФ. □ Сущность не соответствует ЗИФ, если неключевой атрибут определяется другим иеключевьш атрибутом. Мы можем перефразировать второй момент так; Все атрибуты. должны определяться ключом и только ключом. ЗНФ отличается от 2НФ в том, что она имеет дело с отношениями неключевых данных к неюточевым данным. Проблемы те же самые и многие из признаков те же самые, но может выть тяжелее выявить обтпий случай нарушений, с которыми эта форма связана, В основном, главное различие Заключается том, что данные в одном поле вместо зависимости от ключа фактически зависят от даиных в другом неключевом поле. Требования для ЗНФ следующие.
Сущность должна быть в 2НФ Снова это очень важно. Может выть трудно выделить проблемы ЗНФ, .если вы все еще имеете проблемы с 2НФ. Неключевые атрибуты не могут описывать неключевые атрибуты Нели какой-либо из атрибутов функционально зависит от другого неключевого атрибута, то мы снова можем получить проблемы при корректировке данных. Так как юл уже находимся в 2НФ, мы обеспечили, что все наши атрибуты определяются всём ключом, но мы ве рассмотрели отношение атрибутов друг к другу. На следующем рисунке мы возьмем нашу сущность book (книга) и расширим ее, включив издателя и город, где расположен Издатель; book book IS BN number ! title price pubtishfiT_name pubHshef_city Title задает название книги, определенной ключом book_lSBN_number, price указывает цену книги, publisher_name описывает издателя книги, Hopublisher_city не имеет смысла в этом контексте, поскольку непосредственно не описывает книгу. Чтобы исправить вту ситуацию, мы должны создать другую сущность, определяющую информацию об издателе. book jubiisher bookJSBN_number publishcr_name : : publisher_name (FK) i 'titki ; price I publisher_ci*y Теперь сущность publisher содержит только данные, связанные с издателем, а сущность book — информацию о книге. Что здесь особенно ценно, так это то, что теперь, если мы хотим добавить к нашей схеме информацию об издателе, например, информацию о партнерах или адрес, то совершенно очевидно, куда мы добавим эту информацию. Теперь ваш атрибут publisher_city определяет издателя, а не книгу. Когда мы перейдем к физическому моделированию, то обсудим достоинства использования атрибута publisher_name в качестве первичного ключа, но пока это — разумный первичный ключ, и разумный набор атрибутов. Обратите внимание, что решение этой проблемы создало не идентифицирующее отношение publisher —» book. Так как противные атрибуты не были в ключе с самого начала, они не входят туда И теперь.
Все атрибуты должны описывать ключ и итого кроя»ключа Если это выглядит знакомым, то так оно и есть. Это небольшое высказывание — основа для целой группы нормальных форм, связанных отношениями между ключевыми и неключевыми атрибутами, Помните об атом, поскольку это может сэкономить когда-нибудь время при проектировании вашей БД, Проблемы программирования, которые можно избежать с помощью ЗНФ В то время как способы нарушения ЗНФ очень близки к нарушениям 2НФ, имеется несколько важных различи й. Поскольку мы не связаны со значениями ключа» следует рассмотреть отношения каждого атрибута к каждому неключевому атрибуту и так выполнить для каждой комбинации атрибутов. В примере е книгой мы имели следующую структуру сущности: book book_ISBN_numbe' title иве puWsher_name publisher_city В то время как ради краткости я пошел на очевидное нарушение, не всегда так легко определить нарушения ЗНФ. Каждый атрибут следует рассмотреть совместно е каждым ; другим атрибутом. Если сущности имеют разумный размер (я вообще считаю, что десять-двадцать атрибутов в сущности, вероятно, ато столько, сколько вы можете иметь без того, чтобы нарушить некоторые правила нормализации, хотя это и не всегда так), то процесс выявления проблем ЗНФ также не будет слишком длинным процессом. В нашем примере мы должны проверить каждый атрибут е другими тремя атрибутами. Так как имеется четыре атрибута, то мы Должны рассмотреть N * (N - 1) или (4 * 3J = 12 (игнорируя факт, что мы будем проверять некоторые значения больше, чем один раз) различных перестановок отношений атрибутов, чтобы обезопасить себя, В примере с иашей сущностью мы должны проверить: □ title е price, publisher_name и publisher_city; Q price c title, publieher_naiae и publisher_city; □ publisherenamec price, title и publlsher_city; □ publisher_city c price, title и publisher_name. Отсюда мы замечаем, что, когда будем проверять ргЬ1 is her _г.ате с тремя другими атрибутами, становится ясно, 4ropublisher_city функционально зависит от него, следовательно, нарушена ЗНФ. Если вы разработали несколько тысяч сущностей, то начнете различать общие атрибуты, которые могут вызвать проблемы, и нужно будет рассмотреть только несколько атрибутов при ваших проверках нормализации. Мы рассмотрим много таких Примеров В разделе с ртйкым примером этой главы.
Обратите также внимание, что в нашем примере для простоты выбраны специальные названия, чтобы все. выглядело проще, но в действительности названия часто гораздо более загадочные. Рассмотрим следующую сущность: I book 1 ISBW ; ' ' pubname , city - title I ‘ price - I 7.... Эти названия, вероятно, меяее загадочные, чем те, е которыми вы могли бы фактически столкнуться в некоторых традиционных сущностях ВД; однако они уже достаточно неоднозначны, чтобы вызвать проблемы. Атрибут city здесь кажется вполве подходящим, пока вы не поймете, что большинство книг не имеет атрибута city, а издатели могли бы его иметь. Следующий пример кода показывает, что произойдет, если мы захотим изменить атрибут city и сохранить синхронность данных. Возьмем, например, ситуацию, когда мы строим сначала таблицу: I CREATE TABLE book ( ISBN varchax (,2Q) NOT SULL, pubname тагchar Ц91 NOT HULL, city varchar ;8' V.T SC'.'.,, title varthttWOT NULL, price money NOT NULL 1 Здесь имеются нарушения ЗЙФ, которые мы определили. Рассмотрим ситуацию, когда мы хотим изменить поле city для ISB8 15232380237 со значения Virginia Beach (Вирджиния Вич) на Nashville (Нэшвилл). Сначала скорректируем одну запись: UPDATE book SET city = 'Nashville' | «HERE ISBN • '23232380237* Однако так как мы имеем функциональную зависимость для отношенияриЬИвКеЕ — city, далее скорректируем все записи для книг, которые имеют одно и то же имя издателя, на новое значение: UPDATE bock В SET city » 'Nashville* WHERE city « 'Virginia Beach' В AND pubname - 'Phantom Publishing' — исходное имя издателя В то время как это — хороший способ гарантировать, что код групповой операции обновляет должным образом значение атрибута city, так же как и атрибут book, в большинстве случаев этот код затеряетея в вашем приложении, если он ие связан с транзакцией и очень редко выполняет истинную корректировку, Любые сшивки в одном операторе UPDATE — и ваши данные могут быть испорчены. Для существующих приложений SQL Server, которые вы перепроектируете, можно использовать утилиту SQL Server Profiler для проверки, что SQL-запрос фактически послан на SQL Server из вашего приложения.
Признаки, что ваши сущности не удовлетворяют ЗНФ Признаки для ЗНФ очень похожи на признаки для 2НФ, так как они связаны с тем же сортом проблем — быть уверенным, что все неключевые атрибуты связаны только с ключами сущности. Несколько полей с одним и тем же префиксом Заново рассмотрим маш предыдущий пример: ооок '«MJ* — -- S- - птЛ.а»1.пг —Д-j fHJOHSfWr „vTTy x—— _ _ - 7 ’ ~ - a Очевидно, что publisher_name и publxsher_city являются полями e одним и тем же префиксом. В некоторых случаях используемый префикс не будет столь очевидным, например, pub_name, pblish_city или даже location_pub; все это служит серьезным основанием, чтобы установить жфмальньй стандарт обозначении. - Повторяющиеся группы данных Здесь почти то же, что и в 2НФ, но вы должны рассмотреть большее количество перестановок, как мы обсудили ранее. Итоговые данные Овмо из общих нарушений ЗНФ — итоговые данные. Это, когда к сущности-предку добавлены поля, которые ссылаются на записи-потомки и суммирует их. Итоговые даиные были одним из наиболее часто встречающихся зол, с которым мы должны были иметь дело на протяжении всей истории реляционных ВД. Имеется иесколькб новых особ^мостей в SQL Server 2000, которые мы используем, чтобы избежать итоговых данных в нашей реализации, но в логическом моделировании нет абсолютно никакого места для этого. Мало того, что итоговые данные зависят функционально от неключевых полей, они зависят от полей других сущностей. Это приводит к различным видам неприятностей, которые мы рассмотрим. Итоговые данные должны быть сохранены или для физического проекта, или для элементов хранилища данных. Для разъяснения возьмем следующий пример с автомобильными дилерами. Имеется сущность, вядиапуя список всех автомобилей, которые они продают, и сущность, где записываются все автомобильные продажи:80 automobile auwmobite.sate atitwnobile_name automob He_na me (.г К; t Ли айбйюйе safe, number* total sold count total_sold_valua .amcurLscidjcir Зввеь automobile — сущность "явтоиоввль”, autO№obile_gale — еуищжпо? “провозка .. - автомобиля", autooobile_aame — название ввтаиоЛ4л^’впЬжоЬ11е_ял1е^дияЬел — число проданных автомобилей, total_sold_count общее число продаж, tat&l_sald__value — общая сумма продаж, amount_saU_for — сумма за проданный тип автомобиля.
Вместо вычисления общего числа проданных транспортных двдсти и ик стоимости, когда это необходимо, проектировщик решил добавить поля к сущности-предку, которые обращаются и записям-потомкам и суммируют их. Это может казаться приемлемым, но сложность реализуемой системы увеличится по крайней мере на порядок, поскольку мы должны вудеи иметь триггеры у сущности automcbile_sale, которые вычисляют эти значения для любого изменения в сущности auto®obile_sale. Если ио — высокоактивная ВД с частыми записями, добавляемыми к сущности autonioblle_sale, такое обращение приведет к значительному замедлению ВД. С другой стороны, если vro — в основном бездействующая ВД, то в сущности-потомке будет очень немного записей, и выгода от сделанной работы, чтобы быстро найти число проданных транспортных средств и сумму из. стоимости, будет в таком случае очень небольшой. Нормальная форма Бойса-Кодда Во время обсуждения 2НФ и ЗНФ я преднамеренно не конкретизировал слово "ключ". Как я упомянул ранее, ключом может быть любой потенциальный ключ — и первичный ключ, и вторичный ключ. Одвако главная забота 2НФ и ЗНФ — первичный ключ. Нормальная форма Войса-Кодда (НФБК) фактически лучите сконструированная замена для 2НФ и ЗНФ. Обратите внимание, что для обеспечения НФБК не нужно упоминать 2НФ и ЗНФ. НФВК фактически охватывает их обе и определяется следующим образом: □ Все атрибуты полностью зависят ат ключа. □ Сущность находится в НФБК, если каждый детерминант — ключ. Давайте посмотрим на каждое из этих правил отдельно. Все атрибуты полностью зависят от ключа Мы можем перефразировать это следующим образом: Вее атрибуты. З&лжны определяться ключом и только ключам. Это — небольшое, яо важное отиловение от наших предыдущих правил- дня 2НФ и ЗНФ. В этом случае мы не определяем полный ключ, или только ключ — теперь это просто ключ. Чем это отличается? Хорошо вто или нет? Это по существу расширяет смысл 2НФ и ЗНФ, чтобы иметь дело с самой типичной ситуацией, где мы имеем более одного ключа. Было сказано, что атрибут должен полностью зависеть от ключа, и этот ключ должен быть определен как уникальный идентификатор. Уникальный идентификатор нужно рассматривать как адрес или указатель сущности независимооттого, используем ли мы естественный ключ-или нет. Сущность по определению является логическим представлением единственного объекта, реального или воображаемого. Если вы представляете каждый ключ сущности как идентификатор жетона, или номер социального страхования, или полное имя, вы можете понять, что представляет собой каждый из атрибутов.
Например, возьмем человека, который работает иа компанию, и смоделируегл его. Сначала мы выберем ключ, скажем, номер социального страхования. person_______________ soclal_eecurlty_numw Здесь person — сущность "человек", sdcial_security_jiumber — номер социального страхования. Далее начнем добавлять другие атрибуты, которые характерны дан наших служащих — их имя, цвет волос, цвет глаз, номер жетона, который они. получают, номер водительских нрав и т. д. Таким образом, нм имеем следующую сущность: arson________________ aoeW.stcwBrjrartiv first_name height weight badQe^nurnbsr drhrerajlce nse.number ha!r_colot »hoe size Здесь дополнительно first_name — имя, last_name — фамилия, height — poem, weight — вес, badge_ntatber — номер жетона, eye_aolor — цвет глаз, - drivers_Iicense_nuiaber — номер водительских прав, hair_color—цвет волос, - shce_size — размер ботинок. Внимательное изучение сущности показывает, что она соответствует ЗНФ, так как каждый из атрибутов далее описывает сущность, First_name, height, badge_number и другие, — все отвоеятся к сущности. То же самое можно сказать osocial_security_m®ber. Этот атрибут был выбран в качестве ключа прежде всего потому, что это было первое, на что мы натолкнулись. В логическом моделировании выбор поля в качестве первичного ключа не так уж прост, хотя обычно луч» выбрать подходящий, (Более полное обсуждение соответствующих первичных ключей будут приведено в главе 10.) В большинстве случаев, как раз на стадии логического моделирования, мы будем просто использовать искусственный ключ, как рассмотрено в главе 3. Следующее предложение в основном объясняет 2НФ и ЗНФ: Все атрибуты должны далее описать сущность, всю сущность и только сущность. Когда лы поймете ату концепцию, моделирование данных и нормализация станут намного более легкими.
Ошмоепь соответс твует НФБК, если каждый де терминант является ключом Вторая часть определения НФВК говорит, что нужно удостовериться в том, что каждай детерминант — ключ или уникальный идентификатор для сущности. Наше определение детерминанта в главе 3 было следующим: Любой атрибут или комбинация атрибутов, от которых функционально зависит любой другой атрибут или комбинация атрибутов. На основе изучения 2НФ и ЗНФ мы мажем видеть, что это, но существу, определение ключа. Таи как все атрибуты, которые не являются ключами, даяяяы функционя.'п.но зависеть от ключа, определение детерминанта — то же самое, что и определение ключа. НФВК просто расширяет предыдущие нормальные формы, говоря, что сущность может иметь много ключей, и все атрибуты должны зависеть от одного из этих ключей. Мы немного упростили ото утверждение, отмечая, что каждый ключ доджей уникально идентифицировать сущность, и каждый неключевой атрибут должен описывать сущность. Что НФБК подтверждает, так это то, что у сущности может быть несколько ключей. Они упоминаются как потенциальные ключи или вторичные ключи. Одна интересная вещь, которая должна быть отмечена, состоит в том, что каждый ключ является детерминантом для всех других ключей. Это потому, что в каждом месте, где вы видите одно значение ключа, вы можете заменить его другим значением ключа вез потери смысла. Это не говорит О том, что вторичный ключ не может изменять значения — вовсе нет. Номер водительских прав — хороший ключ, но если Департамент автомашин выпускает все новые номера, -это — все еще ключ, н все еще будет описывать сущность, Если значение любого потенциального ключа изменяется, это абсолютно приемлемо. Помня это определение, возьмем пример сущности, которую мы моделируем в этом разделе, и посмотрим на нее снова. «giaLsecwi^jwinW neigM weigh: badge_m(ffiijer eye_coior *йвг«_1еем*лиЛвг drivorsllcensestate heir_cotor Дополнительно здесь <frlvero_license_state — штат. выдавший водительские права. На что мы теперь посмотрим, так это на атрибуты или грунт атрибутов, которые зависят от ключа, и также будут уникальными для каждого экземпляра этой сущности.
First_name отдельно не будет уникальным, н вред ли будет хорошо предположить, что будут уникальными tirst_name и last_name. (Можете ли вы примять его или иет, зависит от размера множества значений. вы, вероятно, хотели бы включить инициал отчества и титул, но это все еще не очень хороший ключ.) Атрибут height описывает человека, но не является уникальяым. То же самое можно сказать и относительно weight. Badge_number, конечно, доджей быть уникален, так что мы можем сделать его ключом. (Заметьте, у нас нет badge_issued_date (дата выпуска жетона), которая будет относиться к жетону и не помогла бы в реализации примера НФБК.) Атрибут drivers_license_nuraber, вероятно, уникален, но вы должны определений рассмотреть изменения штата. Hair_color И shoe_size описывают человека, но ни один из них не может считаться уникальным. Даже если вы берете одновременно height, weight, eye_color, hair_color, и shoe__size человека, вы не можете гарантировать уникальность двух случайных людей. Таким образом, мы смоделируем сущность следующим образом; parton_______________________ socU_security_nufnber Wstjeme tesuwna drtverBjicense.number (AKI) driversjicensestaie (AK1) badga_number (AK2) height weight eye_coior hair_color shoe siza Теперь мы имеем три ключа для этого объекта. Когда: мы будем выполнять физическое моделирование, то выберем надлежащий ключ из ключей, которые мы определили. или используем искусственный ключ. Как рассмотрено в главе 3, искусственный ключ — просто значение, которое используется как указатель на объект, подобно тому» что badge_nwnber — указатель, который использует компания, чтобы идентифицировать служащего, или правительство, использующее номера социального страхования (SSN), чтобы идентифицировать индивидуумов в Соединенных Штатах. Стоит также сказать, что SSN — также не всегда очень хороший ключ. Даже если вы имеете дело только с людьми в США, имеется множество людей, которые не имеют SSN. И, конечно, если вы когда-либо будете иметь дело с людьми, прибывшими в США из-за границы, то SSN никогда не будет работать — послушайте кого-нибудь, кто должен был установить системы с этим условием! Это будет ковечно зависеть от ситуации: сколь много случаев, когда у вас есть SSN, и доля ситуаций, когда, вы имеете дело с SSN или Green Card (документ, разрешающий пребывание и работу в США), чтобы идентифицировать пользователя как жителе США, Ситуация будет всегда диктовать возможное решение, и задачей архитектора данных является выбор соответствующего пути. При выборе ключа мы всегда пробуем удостовериться, что ключи не накладываются. Например, height — не уникальный ключ, но height и badge_nutnber — уникальный! Важно быть уверенным, что отдельные части уникальных ключей не могут быть уникальными сами по себе, иначе могут быта сделаны ошибки, если вы случайно помещаете неуникальные данные в столбцах, которые должны выть уникальными.
Признаки программных аномалий Признаки определения, что сущность находится в НФЕК, — те же самые, что и для 2НФ и ЗНФ. Аномалии программирования, устраняемые с помощью НФВК, также те же самые. Главный момент, который нужно здесь упомянуть, — то, что или вы получаете все ваши детерминанты, смоделированные в течение этой стадии проектирования, то когда вы будете реализовывать ВД, наиболее вероятно, детерминанты будут .реализованы как уникальные ключи. Это предотвратит пользователей от ввода неуникальных значений в столбцы , которые должны содержать только уникальные значения. Это заканчивает наш краткий обзор первых четырех нормальных форм. Мы рассмо трим Четвертую и Пятую нормальные формы в следующей главе, но сейчас мы реализуем только что изученные концепции в нашем учебном примере. Учебный пример В этой главе мы пройдем каждую из нормальных форм и исправим нашу модель согласно полученным правилам, затем перейдем к окончательной структуре. Первая нормальная форма Наша модель имеет несколько нарушений 1НФ. Мы выделим каждое из нарушений, которые могут иметь место. Атрибуты должны быть элементарными У нас есть один пример этого вида нарушения. Оно возникает в сущности statement,.Мы должны вспомнить, что эта сущность представляет отчет, который банк посылает каждый месяц, чтобы клиент мог проверить, все ли веданные чеки действительно имели место. Атрибут balancinglteae содержит все сделки, которые банк записал, и они должны совпасть с элементами регистра. Однако этот атрибут будет содержать много строк и много полей, которые вряд ли являются элементарными I statement ] ’ bankld Pi ima: у Key (FK) f statemeritld PrtmaMtey | tjpe: Соей [ prcviousBalance: Amount [ previousBalanceData: Date current Ba I a nee: Amount ! statemertDate: Date j . let a i Debits: Amount iDtatCreats Amount , . balancngltems: Balardnglt Таким образом, мы должны добавить новую сущность, чтобы разместить эти элементы. Поскольку мы не внаем точно, что войдет в эту сущность, мы позволим атрибутам мигрировать из сущности statement, а затем добавим другой указатель к первичному ключу для его уникальности.
statement________ bertckJ: PrimgryKey (FKJ - statements: PrimaryKey_________ type: Code pra-iousBaiance: Amount pr&TOUsBaianceDate: Date cuTentBalanoe: Amount staternentDate: Date total Debits: Amount total Cred its: Amount balancing Ite ms: Balancing! terns. имеет элементы в state me nit tern *___________ ’’"bankldt Primary-fey (FK) statementid: Primary fey (FK) ' statementitem Id: Primaryfey Нужно рассмотреть другие типы нарушений этого же вида. Возьмем сущность payee (получатель платежа) и два поля: address (адрес) и phoneSwnbar (номер телефона). В нашем примере мы рассмотрим только атрибут address. Адрес состоят из нескольких частей: строк уличного адреса (овычио мы довольствуемся двумя строками, чтобы хранить уличную информацию), города, штата и почтового индекса. Мы также расширим поле phoneNumber (номер телефона) так, как обсуждали ранее. = Как только мы закончим этот процесс, то получим следующий результат: fSXSS______._______„ payee id: P-imaryKey - I name: Name (AKI .1) < addressUnel; Address Descriptive Line i , edcif«sUne2: Address Descriptive Line । I ! addressCity: City : I addressState StateCode I addressZipCode: ZipCoce I phoneCcunuyCode: Country-Code '' phoneAreaCoctei AreaCode phoneExchange' Exchange phoneNumber. PhoneNumberPart - phoneSttertston: Extension В этом примере теперь нет никаких полей, которые очевидно нарушают 1НФ и поэтому не элементарны. Фактически, мы могли бы иметь случай, что noneaddressLinel не является элементарным, так как оно будет содержать номер улицы, название улицы и другие части информации. Однако мы ие собираемся моделяровать^эти йтрйВутВт в нашем примере - ' диаграммы, стараясь сохранить «еид простыми.
Заметьте, что мы создали домены для каждого нового атрибута, Атрибуты addressLinel и addressLine2 — одни и тот же вид элемента. Сделаем также замечание относительно атрибута phoneNurober. Он имеет то же самое название, что и прежде, но другой смысл, так как номер телефона состоит из койа страны, кода региона, телефонией станции и номера. Поскольку он имеет другой смысл, мы создали новый домен е новым названием, так как практически все еще могут быть сущности, которые используют исходный домен. Все экземпляры записей сущности должны содержать одно и то же число значений В нашей новой сущности payee (получатель платежа) мы объединили вместе общий набор атрибутов, которые нарушают эту часть правила — помaddressbinel и addressLineZ. В то время как это обычное решение проблемы адреса, оно там не менее является нарушением. Наличие установленного числа строк адреса кусало меня несколько раз, когда адреса нуждались в большем числе, иногда даже в четырех или пяти строках. Таи как не каждый адрес будет иметь то же самое число элементов, это — проблема. Мы решаем ее, добавляя другую сущность-потомок ДЛЯ информации о строках адреса: раус-е payeeid: P-imaryKey name: Name (АК1.1] аИгемСйу: Oity addressStste StateCode addressZipCode: ZipCode phoneCountr у Code: CountryCode phoneAreaCode: ArsaCode phone Exchange: Exchange phoneNumber; PhoneNcmberPart phoneEsoenslon: Extension имеет строки адреса в payeeid: Prim агу Key (FK; payeeAddressId: Primary Key a-ddressUne: AddressDescrintiveUne \ sortOrderid: SoriOrperid__________ Это может показаться немного странным, но получается именно так, рассматривая путь, которым ВД разрабатывались в течение длительного периода времени. Однако такое проектирование адреса дает нам гибкость, чтобы хранить столько частей информации, сколько потребуется, вместо необходимости добавлять столбцы, «ли ситуация потребует этого. Это также является хорошим примером нормализации, создающей большее количество сущностей. Все экземпляры записей сущности должны быть различными Мы начали заботиться об атом, добавляя первичные ключи и вторичные ключи к нашим сущностям. Обратите внимание, что просто’добавление искусственного ключа не будет обеспечивать этого специфического правила. Одна из наших последних задач физического моделирования должна будет проверить, что все наши сущности имеют, по крайней мере, один определенный ключ, который не содержит немигрирующий искусственный ключ. Нормальная форма Бойса-Кодда Так как НФБК фактически является расширением 2НФ и ЗНФ, мы можем рассматривать все нарушения и обсуждать их совместно.
Итоговые данные Поля итоговых данных вообщи-тэ самы? легкие для устранения нарушения. Это потому, что мы можем обычно просто удалить значения из наших сущностей. Например, в сущиости account (счет) мы можем удалить атрибут Running Total (текущий итог), поскольку оя может быть получен, подводя итог значений, которые запасены в сущности transaction (сделка). Это оставляет нам следующее: f account I ijarWS: ftimar>Key (Ий- aecountfcfc mmarAey питое- Atpnalta№teMumoer (AKI .1) ba'anceDase: Sait С другой стороны, мы имеем ситуацию, когда итоговые поля находятся в сущности statement (отчет): ! statement_______________________ i bankid: Primaryfey (FK) stetemantld: МиагМСщ______________ type: Code preMtoasBatance: Amount previousBalanceDate: Date currentBalance: Amount statementDate: Date j tots I Debits Amount I - .. totalCredits Amount j balandrg^ems: Baiancinflltems j j Мы имеем previousBalance, cur rent Balance и т. д. — фактически, все поля, кроме type (тип), ссылаются на некоторые другие значения сущностей. Однако в данном случав мы могли бы согласиться с этим. Поскольку сущяо«ь-веапел1вп.р-<^чвт)._1В«я»те_двкум«н»рм, а именно, документом, формируемым банкой, эти поля могли бы пригодиться, так как мы можем разместить здесь то, что банк дуа.-ает об отчете. Мы, вероятий, будем желать иметь эти поля и использовать их, чтобы проверять данные, которые будем хранить в сущности statementitem (элемент отчета). Несколько полей с одинаковыми префиксами Мы имеем очень хороший пример этого вида проблемы в созданной нами сущности payee (получатель платежа). Атрибуты phcneCountryCotfe, phoneAreaCode и т. д., все имеют один и тот же префикс (обратите внимание, что это не всегда будет стойь очевидным, как мы говорили ранее); аналогично для addreasCity и других атрибутов. иа«е payee! г PrimaryKey I ~ : ’ name: Name (AKI .1) | aftressCity. City . - , . - ; adaressState StateCode 1 addressZipCoda: Z^Cede [ = pdoneCount'yCode: CountryCotie | pdoneAreaCode: AreaCode < ' pironetscha-ge-£>iCtianga ' H r - ргюгеКитЬег PhooeNumbciPan j : phonefatenslafl: Ейепявп . ... . . ( . 7 -1868 175
Телефонные номера и адреса — фактически -объекты сами по себе. Каждое и» телефонных ' полей фактически не описывает сущность payee (получатель платежа), но обеспечивает существование телефонного номера. То же самое относится и к полям адреса. Следовательио, мы развиваем их следующим образом: address sddressid Рпта-уКвУ определяет расположение city: ЭД - state. SiateCods apCcdeZipCode имеет дополнительную информацию s ''addressld: PnmayKey (FK? sdaressUneki: Pr^mar vKev addreMune: Andress PescdpweL г,s IwtOtasrta: ВанОгаегИ_________ Теперь мы удовлетворили идею, чтобы каждое поле обращалось к ключу, поскольку address и phoneHumber относятся к адресу и телефонному номеру получателя платежа, address определяется городом, штатом и почтовым индексом плюс уличная информация в сущности addressLine, Сущность phoneNumber также определяется своими атрибутами. Каждый детерминант должен быть ключом Детерминант — то место, где может возникнуть ошибка. Рассмотрим супщостьрауее из надето предыдущего примера. Атрибут payeeid является первичным ключом, a name — детерминантом сущности. Как мы ранее рассмотрели, имеется следующий нявор зависимостей: □ Для каждого значения payeeid вы должны efftTb то же самое значение name, О Для каждого значения пате вы должны иметь то же самое значение addressld и phoneRumberld. □ Не обязательно, чтобы для каждого значения addressed и phoneNumberld вы имели то же самое значение пате, поскольку адрес или телефонный номер могут использоваться для нескольких получателей платежа. Реальная проблема здесь в выборе соответствующего набора ключей. При использовании искусствеиного ключа в вида указателя уже на отадаи логического моделирования можно решить основную проблему, Каждая сущность должна содержать уникальные значения, и это уникальное значение яе должно включать только одао бесполезное значение, в то время как искусственный ключ бес-иол езен за исключением использования в качестве указателя к самому себе. В случае, где есть более одного ключа, мы должны определить потенциальный ключ из мигрирующего ключа и другого атрибута сущности.
Сначала возьмем сущность phoneMumber: phone Humber phaneNumberld: PrmaryKey eountryCodeCountryCodB areaCode: AreaCode exchange: Exchange number: PhoneNumberPart extension Extension Она ив имеет никакого определенного ключа, так что мы должны его сконструироаать, В атом случав вея сущность без первичного ключа будет представлять ключ. Это, кстати, иллюстрирует одау йз основных причин- иметь отдельные тематические сущности. Теперь, когда мы вкщелили телефонный иомер, мы можем выть уверенными, что если два получателя платежа имеют те же самые телефонные номера, мы же будем дублировать эти значения. Делая это, мы сохраняем компоненты понятными дяя последующего использования этих значений, в то время как имея двойные телефонные номера, все будет более запутанным. pnotteNumber shone Number id PrimaryKey countfyCode CourtryCoce (ДК1.1) areaCode: AreaCode (AKI ,2) exctiange: Exchange (AK1.3J number: PhoneNumt'erpB't (AK1 .< eHenstai: Extension (AKI.5) Далее рассмотрим сущность address: address addressid: Primary-Key city: Сгту state: StatsCede uipCode ZipCode addressLine имеет дополнительную информацию в ^Sdressld: Primary Key (FK) add reasu л eic: Primary Key addressLine: Add3>ssOe scrip tr.-eUrie J Это — очень интересный случай. Нет никакого способа, которым можно сделать city, state и zipCode уникальными, поскольку мы будем дочти всегда иметь больше чем один адрес одного и того же типа. В этом случае уникальность определяется атрибутами city, state, zipCode плюс любые элементы addressLine в подходящем сочетании. Мы можем не иметь уникальности на основе значений сущности addressLine, поскольку это лишь логическая часть сущности address и address ссылается на нее для уникальности. Нельзя смоделировать ситуацию, когда только один адрес может содержать информацию "101 Main Street". Это не обязательно. Мы не будем реализовывать эту ситуацию именно таким образам, поскольку ото слишком трудно. Яо логически в данном случае это наиболее подходящий путь моделирования данных.
Наконец, рассмотрим сущность transaction: Mnsaction fbankld: PrimaryKey ffKj accnurm WmatyKej ffK) i trarisatoomd Primary Key__________ • rtata Date j number: AohaNumerictomber (AKIЛ) ! Ascription: Descriptor amount: Amount type: Code_______________________ В этом наборе атрибутов мы имеем ключ, который составлен полностью из миградующих ключей (bankid, accountId) и еще одного искусственного ключа. В основном, назначение его в том, что мигрирующий ключ счета определяет часть сделки, и ням нужно разовраться с соответствующим дополнительным атрибутом. В атом влучаяюн будет числом, и поскольку он является числом, то Данн использует его, чтобы идентифицировать сделку, и мы предварительно выбрали его как собственный ключ: transaction tenWd: PrtrnerjiCef {FK- (AKI .1) ] accounBd: PrimaryKey (FK) (AKI ,2J ' transaetionW: РигмуКеу_____________: data Date - - [ number: A^haNwnerte&imber (AKI 3]! desr-iptior: Description | amount: Amount j (type: Code-j Sjint-e мы должны пройти каждую другую сущность, чтобы убедиться, что иазначен надлежащий ключ, и, что более важно, нет никаких атрибутов, которые функционально зависят от другого атрибута. Конечно, это то же самое, если бы мы определили достаточно атрибутов в данный момент, как и в случае statementItem, но не определили никаким значений. Теперь учтем также, что bankid, accc=untld и transactionld функционально определяют атрибут number, a bankid, accountld и number функционально определяют transactionld. Это — подходящая ситуация, которая является довольно интересной! Одно дополнительное замечание следует сделать относительно сущностей-подтипов сущности transaction: check ~ _ fbanHd: Pr i mar у Key (FK) * ассоигШ: Prirr aryKey (FK) transactionig; PrimaryKey (FK) i pafeeM: PrimaryKey (FK) l usageType: СоЛ (signage: №0_______________ Здесь не требуется никаких вторичных ключей, так как эта сущность находится в отношении "одаи к одному" с сущностью transaction, следовательно, первичный ключ является подходящим ключом.
Модель Вот наша модель после нормализации вплоть до НФБК. Обратите внимание, что многие ва общих аномалий корректировки должны быть выяснены. Это далеко не полно, но тем не менее.., Чтобы модель была простой, не будем добавлять никакой дальнейшей информации к ней. Мы представим заключительную модель с дополнительными полями * последнем разделе части книги, посвящённой логическому моделированию. f SifeJM: »»t«T П ^Й£«^И*«: ______ АдаМ«Ый^ ИЕ1; И Prwga^y яма *t.iw i6"*' Msas*i=ytRq SHrftf ГАК1 1i ^irFiinm еакщ мяМг: .4} пшжааг: .JtphafiMnwieHwwaf- УМС t -S> Me (HG 8Ж«йй: Frt5Sff»to¥ гЛт 'iTi'f*Tiii' «ВЙ______S__________ .кщ^ДСЙ^ДгУйТ^Хю^ЯЭ CT^gftT tfiQ айдВпжГЙтЯС gear® nPnK'+lgy ffs .. .iri.irj > r-1-i iii. Резюме В этой главе мы начали процесс превращения наших случайных структур таблиц в структуры, которые будут обеспечивать хранение данных намного более надежно. Строя ати структуры в очень конкретной манере, мы получим набор сущностей, которые являются эластичными для изменений, что приведет к уменьшению аномалий при корректировке. Внешне все это может казаться одним из наиболее загадочных процессов в информатике. Это, вероятно, обменяет, почему многие разработчики не нормаливуют евои ВД. Однако для атого нет никакой причины. За пределами иногда тайного, понятного только посвященным, жаргона все его представляет набор простых, хорошо сформулированных правил, которые устанавливают, как нужно разрабатывать ВД. Что касается окончательных результатов, учебный пример должен был продемонстрировать, что окончательные сущности, которые мы разработали, более чистые, более опрятные и более безопасные для ввода наших данных, даже при том, что размещаемые данные будут выглядеть все менее и менее значащими для случайного наблюдателя.
Теперь очевидный вопрос; можем ли мы нормализовать наши данные каким-либо образом дальше? Так как я предупредил вас о том, что имеется семь нормальных форм, а мы рассмотрели только четыре, вы знаете, что ответ на этот вопрос утвердительный. С другой стороны, следует ли нам нормализовать далее? Да! ЗНФ в течение длительного времени рассматривалась как наиболее важный результат моделирования данных, и, может быть, когда мы будем физически реализовывать маше решение, мы не пойдем далее этого уровня. Однако мы пока находимся иа логической стадии моде л и рл на н и я, и ие должны интересоваться проблемами реализации; мы должны стремиться к идеальной модели, Разработка следующих нормальных форм раскроет дополнительные проблемы е пешими данными, которые мы не увидим иначе.
Расширенные темы нормализации Введение Если вы слышали эта однажды, то вы, вероятно, слышали ио миллион раз: "Эта ВД находится в ЗНФ, так что давайте строить таблицы'” В этой главе мы рассмотрим дальняй-пие методы нормализации, которые являются столь же важными, как и первые три, хотя они обычно и не используются так иифоко из-за имеющихся недостатков, как в смысле времени, не-эбходимог-о для шс реализации, так и стоимости создания результирующей БД, Мы рассмотрели в предыдущей главе некоторые различные аномалии программирования, которые 8НФ исправляет, но, как мы обсудам ниже, могут все еще быть некоторые проблемы, остаю^еся в нашем логическом проекте, главным образом, вызванные наличием трс-йных отношений. В сущности, так как большинство людей думает, что они завершают процесс нормализации при достижении ЗНФ, в чем они дейстаительно должны выть заинтересованы, «к это в получении в своих логических моделях, по крайней мере, 8НФ, Нужно решить какая требуется степень физической реализации ироекта, и определить, какой уровень нормализации является соответетвуюищм атому, Однако как общий руководящий принцип, проектировщик должен всегда пытаться нормализовать все сущности в столь высокой форме, насколько эт возможно. Если при испытании системы обнаружатся проблемы е реализацией, то систему можно денормализовать, о чем будет сказано позже в этой главе, а также в главе 14. Эта глава, по моему мнению, наиболее сложна для понимания. Методы а зтой главе чрезвычайно легко выполнить, и вели л выполнил свою работу, то и они также легки для понимания. Имеется несколько очень серьезных неправильных представлений относительно того, что нормальные формы, после третьей являются бессмысленными, и они очень неправильны, Я чувствую, что, как только вы. фактически нормализуете Четвертый уровень, то даже не будете рассматривать возможность возвращения.
Четвертая нормальная форма Наши правила нормалшзаиии пока решили проблему иабытка среда столбцов в сущности, но не решили проблем, которые являются результатом сущностей, имеющих сложные первичные ключи, и пока все еще обладают избыточными данными между строками. Нормализация сущностей с помощью Четвертой нормальной формы (4НФ) занимается такими проблемами, В простом случае переход к 4НФ будет связан с проблемами типа моделирования атрибута, который должен хранить единственное значение, но завершается размещением нескольких значений. Второй тип проблемы более неуловим. Рассмотрим сущность учащихся в классе (обратите внимание, что несколько классов представляют совсем другие проблемы). Мы должны хранить преподавателя каждого учащегося, поэтому поместим это в сущность clasaAssignment следующим образом: ctassAssignment class teecner student Здесь classAssiantnent — еущкоенп "состав класса', class — класс, teacher — преподаватель, student — учащийся. Однако это неудовлетворительно, так к« мы имеем связанные атрибуты class, teacher и student в одной и той же сущности. Чтобы привести это к 4НФ, мы должны развить это- троичное отношение. С этой целью мы будем считать, что центром отношений является атрибут class, так как класс имеет одного или большее количество преподавателей и, разумеется, одного или большее количество учащихся. Наш измененный проект теперь выглядит следующим образом: classTeacher Этот пример иллюстрирует, что для сущности должны быть выполнены следующие условия, чтобы находиться в 4НФ: О Сущность должна быть в НФВК, Это условие гарантирует, что все ключи будут соответственно определены, и все значения в сущности будут соответственно зависеть от ее ключа.
□ Не должно быть больше одной зависимости со многими значениями, представленной в сущности. Не более одного атрибута может хранить несколько значений, которые связаны с ключом в любой сущности, иначе будет дублирование данных. Кроме того, должно быть обеспечено, что мы не повторяем атрибуты с одним значением в каждом атрибуте с несколькими величинами. Мы посмотрим иа несколько примеров, чтобы помочь сделать эти идеи более ясными, Давайте сначала рассмотрим три основных вида нарушений 4НФ: □ тройные отношения; □ скрытые атрибуты с несколькими величинами; □ состояние и другие атрибуты, для которых мы должны знать предыдущие значения. Тройные отношения Мы кратко рассмотрели тройные отношения ранее в главе 3. Не все реальные отношения проявятся в отношениях с двоичным типом, и тройные отношения действительно являются весьма общими. Любые три (или больше) идентифицирующих или обязательных неидентифицирующих отношений в сущности, вероятно, вызовут неприятности. Возьмем в качестве примера ситуацию, кода мы разработали набор сущностей для обеспечения планирования презентаций, размещая информацию в сущностях session (презентация), presenter (презентатор) и room (помещение), где должна произойти презентация: Здесь raomSesBionPrasenter сущность- ”-Пом^ени*-пуезентациягпреаен,гаатар“. Предположим также, что должен выполняться следующий набор очень распространенных бизнес-правил: □ В список может быть внесен более чем один презентатор, осуществляющий презентацию. □ Презентация может занимать более чем одно помещение.
Следующий рисунок моделирует отношение: 'презентатор представляет презентацию в иомещвижж". presenter room Звееь room — сущность "помещение", roomld — идентификатор помещения, roomName — название помещения, presenter — сущность "презентатор", presenterld — идентификатор презентатора, firstftame — имя, lastName — фамилия, roomSessionPresenter — сущность " помещение-презентация-презентапюр", rGomSossionProsenterld — идентификатор помещения-презеятации-презентатора, session — сущность "презентация", sessdonld — идентификатор презентации, пяте — название (презентации), startTime — время начала, endTime — время завершения. Напоминание: каждая из этих величин мажет иметь значение NULL, как обозначено ромбом на противоположном конце соединяющей линии. Каждая иа этих сущностей находится и НФВК; однако отношения между этими тремя сущностями сомнительны. Посмотрим на набор типовых данных: Session (презентация) Presenter (презентатор) Room (помещение) 101 Davidson Biver Room 202 Davidson Stream Room 202 Hazel Stream Room 404 Hawkins Brook Room 404 Hawkins Stream Room
В первой строке нет никаких проблем, поскольку мы имеем одну строку для атрибута session — 101, который имеет один атрибут presenter —Дэвидсон и один атрибут гсот — River Room. Проблема возникает в следующих двух строках, поскольку один атрибут session — 202 имеет два различных атрибута presenter, яо все же один атрибут room. Это вынуждает нас лишний раз повторить данные в атрибуте room, так как мы теперь храним в двух местах то, что session — 202 находится в Stream Room. Если session перемещается в другое место, мы должны изменить это в двух местах, н если забываем об этой особенности и корректируем room значением, которое в настоящее время не отображаем (например, с помощью искусственного ключа), то получим: 202 Davidson Stream Room 202 Hazel "изменено на Room" В этом примере мы дублировали данные в атрибутах eeselon и room, а для атрибута session, равного 404, дублируются данные атрибутов session и presenter. Реальная проблема с нашим сценарием возникает при добавлении или изменении наших данных. Если мы должны корректировать значение атрибута session, которое используют Davidson с Hazel в Stream Room, то потребуется изменить две строки. Аналогично, если изменяется значение room, потребуется изменить несколько строк. Когда сущности реализованы таким способом, мы можем даже не получить все строки, полностью заполненные, как эти. В следующей сущности мы видим набор строк, которые являются функционально эквивалентными набору в предыдущей сущности. Seeeion (презентация) Presenter (презентатор) Room (помещение) 101 Davidson <null> 101 <nuU> River Room 202 Davidson <null> 202 <null> Stream Room 202 Hazel <null> 404 <null> Brook Room 404 Hawkins <null> 404 <mill> Stream Room В этом примере имеются пустые указатели для некоторых значений атрибута room и некоторых значений атрибута presenter. Мы устранили дублированные данные, но теперь все, что мы имеем — некоторые довольно странно смотрящиеся данные с пустыми указателями повсюду. Кроме того, нельзя разумяо использовать пустые;укамтелк в ситуации, когда мы не анаг-м аиачения сущности presenter для конкретного значения сущности session. Мы фактически размещаем набор данных, эквивалентный данным в предыдущем примере, но е данными в иой форме очень трудно работать.
Чтобы получить ре шение этой проблемы, етачала возьмем сущность presenter в качестве первичной сущности: Офаиште вншиамие, что наша первоначальная реализация была неидентифицирующим отношением. Чтобы подучить диаграммы, настолько понятными, насколько возможно, посмотрим на отношения в терминах идентифицирующих отношений. Для этого мы возьмем данные в сущности roomSessionPresenter и разобьем их иа следующие сущности: Presenter Davidson Hazel Hawkins Presenter Boom Davidson River Room Davidson Stream Room Hazel Stream Room Hawkine Stream Room Hawkins Brook Room Presenter Session Davidson 101 Davidson 202 Hate! 404 Hawkins 404 Это, очевидно, не лучшее решение, потому что мы никогда ив вмотли вы определить, в каком помещении (room) проводится презентация (sesaion), пока не будет назначен презентатор (presenter). Аналогично, Uamdstm организует презентацию в Kvar Room, а также и в Stream Room, и нет никакой связи с презентацией (session), которая проводится в соответствующем помещении (room). Когда мы разбиваем любые отношения и при этом теряем значения данных, такое разбиение называется декомпозицией с потерями. Это один из таких случаев и не лучшее решение нашей провлемы.
Далее мы попробуем сосредоточиться на помещениях (room), в которых проводятся презентации: roomSession Г room (FK) [session roornPresenter room (FK) presenter Взяв данные и поместив их в сущности еще раз, получим: Room River Room Brook Room Stream Room Room Presenter River Room Davidson Stream Room Davidson Stream Room Hazel Stream Room Hawkins Brook Room Hawkins Room Sessioa River Room 101 Stream Room 202 Brook Room aoa Stream Room 404 Снова это — декомпозиция с потерями и также неподходящее решение, потому что мы не епосойны определить, например, точно, кто представляет№2 презентацию (session). Это происходит в Stream. Boom, a Davidson, Hotel и НаиМпа — все представляют в Stream Шот, но они не все представляют 202 презентацию. Поэтому мы должны еще раа рассмотреть другой проект. На сён раз мы сосредоточиваем наш проект на проводимых презентациях:
Посмотрим на данные: Session 101 202 404 Session Room 101 River Room 202 Stream Room 404 Brook Room 404 Stream Room Session Presenter 101 Davidson 202 Davidson 202 Hazel 404 Hawkins Наконец-то мы натолкнулись на решение проблемы. Ив этих данных мы способны определить точно, кто что представляет и где, и мы не вудем иметь никаких проблем, добавляя или удаляя презентаторов или даже изменяя помещения. Возьмем, например, презентацию (session) 404, Мы имеем следующие данные в сущностях sessionRoom и sessionpresenter для этой презентации: Session Room 404 Brook Room 404 Stream Room Session Presenter 404 Hawkins
Чтобы добавить презентатора по имени Evans к списку, мы просто добавляем другую строку: Session Presenter 404 Hawkins 404 Evane Таким образом, это — надлежащее разложение, и не создаст нам проблем в нашей первоначальной сущности. Наша окончательная модель выглядит следующим образом: Теперь, когда мы задаем сущность session отдельно от сущности presenter, и пустые указатели больше не требуются в значениях внешнего ключа, то если хотим показать, что помещение (room) не было выбрано, то просто не создаем запись сущности sessionиоо:т. Так же мы поступаем, если пока не выбрали презентатора .(presenter).. Более важно, что мы теперь можем задавать несколько значений room для значения сущности session вез возможных нарушений, и мы, конечно, можем не дублировать значения сущЕюстей session и room. Если мы должны иметь дополнительные данные, которые расширяют понятие сущности sessionpresenter, например, обозначить дополнительного презентатора (presenter) (или даже первого и второго презентатора), мы теперь имеем логическое место, чтобы хранить эту информацию. Обратите внимание, что, если бы мы пробовали хранить эту информацию в первоначальной сущности, это нарушило бы НФВК, так как атрибутагхегаасеРгезелсег будет связей только с session и presenter, но не с room. Скрытые атрибуты с несколькими величинами Мы рассмотрим некоторые атрибуты, которые назовем скрытыми, иотему что они на первый взгляд не создают проблем. Чтобы попытаться проиллюстрировать эту ситуацию, рассмотрим следующую модель проекта: (Используемые идентификаторы см„ например, а главе £>.)
Здесь возникает проблема, когда мы рассматриваем сущность contact (партнер), так как мы имеем три атрибута: имя (name) партнера (предполагаем, -что этот атрибут находится в 1НФ), номер телефона (phone) и адрес (address), Имя не создает проблем, поскольку каждый партнер (contact) имеет единственное имя, по которому мы обращаемся к нему, но в настоящее время много людей имеют бояыпе одного адреса и номера телефона! Следовательно, мы имеем атрибуты о несколькими значениями и требуется их дальнейшая нормализация, чтобы разрешить возникающие проблемы. Чтобы учесть такие адреса и телефоны с несколькими значениями, мы могли бы изменить наш проект следующим образом: contact address Пока наличие нескольких номеров телефона на является нарушением 1НФ (так как все они являются различными типами номеров телефона, а не разными значениями одного и того же типа), и мы должны ожидать проблем лишь в будущем. Так как мы просто добавили тип атрибута к его имени (например, hoeeAddressld, f«Phone Id), в дальнейшем мы можем получить проблемы с атрибутами, имеющими несколько значений, если, например, партнер имеет два факса или деже два мобильных телефона. Кроме того, мы находимся в неприятной ситуации, когда может потребоваться значение NULL для каждого иа атрибутов, когда они ие существуют. Это неудачное представление отношений. Например, если клиенту потребуется атрибут но .мера телефона офиса супруга (супруги) партнера, мы должны будем изменить модель, что, по всей вероятности, приведет нас к переписыванию кода приложения. Изменим далее проект таким образом, чтобы иметь отдельные сущности contact (партнер) и contactinformation (информация о партнере): Атрибут type (тип) обозначает тип информации о партнере, которую мы размещаем в экземпляре сущности, так что мы теперь можем иметь экземпляр сущности contactinformation с типом (type) Ноте (домашний) и добавить сюда адрес и номер телефона. Это теперь позволит нам добавлять столько номеров телефона и адресов, сколько потребуется пользователю. Однако так как адрес и телефон размещаются в одаой и той же таблице, нам потребуются значения NULL там, где партнер имеет различное число домашних адресов и номеров телефона.
На агой стадии мы должны принять решение относительно того, что хотим сделать. Мы кожей пожелать, чтобы номер телефона выл евизан е адресом (например, объединение домашнего адреса <з домашним номером телефона). В нашем случае мы вделаем следующее: разовьем сущность contactInformation иа сущности contactAdaress-и contactPhone (хотя это м не следует рассматривать как единственно возможное решение провлемы): Эта корректировка устранила зависимости с несколькими значениями, так как мы теперь можем иметь несколько адресов и несколько номеров телефона независимо друг от друга и можем определить столько типов, столько потребуется, без необходимости изменения наших структур сущностей. Однако мы можем сделать еще один дальнейший шаг, моделируя номер телефона в адрес как отдельные сущности в логическо! модели, и добавить домены сущностей для типов столбцов. Таким образом, мы можем предотвратить пояьзователев от печатания "Домашний", "Домовой", "Дом.” и т. д., когда они подразумевают "домашний”. Это также даст нам пользовательское конфигурируемое ограничение, так что мы можем добавлять дополнительные типы без необходимости наменять модель. Мы добавим атрибут description (описание) я сущностям доменов, позволяющий нам описать фактическую цель тина. Это учитывает такие ситуации, когда мы имеем тип адреса "На стороне", который является симпатичным стандартом для данной организации, но запутывающим пользователя в первый раз. Описание типа: “Адрес партнера при путешествии в расширенных коммерческих посадках" тогда могло бы быть иазначено. Наша заключительная модель теперь выглядит так:.
Следует отметить, что я вделал дополнительные атрибуты сущностей address (адрес) и phoneWumber (номер телефона) вторичными кличами, чтобы избежать дублирования адреса каждый раз, когда ои используется в системе. В этом случае, если мы имеем пять партнеров, которые имеют один и то» же адрес их офиса, нужно изменить адрес только в одном месте. То, что напоминает массовое убийство, может поэтому фактически дать выгоды, хотя, когда вы начнете физическое моделирование этих сущностей, нужно будет решить, следует ли реализовывать «от уровень, имея в виду проблемы настройки, которые могут возникнуть. Предыстория атрибутов Мы можем также столкнуться о проблемами у нашей модели проекта в ситуации, когда нужно сохранить тип состояния информации для экземпляра некоторой сущности. Например, в следующей диаграмме мы создали две сущности, которые размещают характеристики заказа, а также суадюоть домена для состояния заказа: order । ofdcfld ~~ | oraerStatusTypeid (FK) ! taken Date ! ve-inedDate 1 sent'fc®TOp«gDate i fWBWDate j sentToOingDate j biiledDate ' ! paymentRaceivedDate [ orderStatusTypa j orderStawsTypeid i name Здесь order — сущность "заказ”: orderld — идентификатор заказа: takenDate — дата принятия; vsrifiedDate — дата проверки; sentToShippingDate дата отгрузки; biiledDate — дата выставления счета; paymentKeceivedDate — дата получения оплаты; crderStatusType — сущность "тип состояния заказа’; orderStatusTypeld — идентификатор типа состояния заказа; паше — название. Проблема здесь состоит в том, что изменения состояния заказа (и, следовательно, значение атрибута orderStatusTypeld) связаны и ео значениями дат- в других поляк, и с другими внешними факторами. Например, когда заказ принят от заказчика, атривутдакепОапе следует заполнить текущей датой. Заказ тогда мог вы иметь состояние "Незаконченный". После того, как система оплаты заказчика была проверена, мы изменим атрибут verifiedDate иа дату проверки и установим состояние на "В процессе". Состояние "В процессе" может фактически означать больше чем одиа какая-либо операция, типа "отгрузка" или "выставление счета". Нас интересует здесь атрибут orderStatuslypeld в сущности order. Он содержит текущее состояние для экземпляра заказа, Как вы ответите на вопросы относительно того, когда заказ послан отделу отгрузки, или когда отдел проверки заказа проверил заказ? Разработчик моделей данных добавил бы несколько полей в сущность order, чтобы разместить «и части информации, но что вели проверка однажды потерпела неудачу? Интересует это нас? И что означает дата fulfilledDate — когда заказ был или полностью отправлен, или отменен, либо, строго говоря, тогда, когда он был полностью отправлен, так что мы должны добавить другой атрибут — canceledDate (дата отмены)?
Чтобы решить эту проблему, мы должны будем изменить нашу модель, чтобы позволить хранение нескольких значений для каждого из атрибутов, которые мы еовдяли. orderstatus order orderStatusId orderStatusType . orderld * orterid (FK) orderStatusTypeid | orderStatusTypeld (FK)( AKI) J name j L_ effectiveDate (AKI) ЗЭесь дополнительно orderstatus — состояние заказе; arderStatusId = идентификатор состояния заказа; effectiveDate — аффективная дата. Теперь всякий раз, когда изменяется состояние заказа, все, что мы должны сделать — это добавить запись в сущность orderstatus. В любом случае вались с последним значением атрибута effectiveDate — текущее состояние. Это также позволило бы нам иметь больше одного значения состояния одновременно. Например, не все состояния осуществляются последовательным способом. В сущность order Stat us. например, вы можете поместить информацию о проверке счета-фактуры, затем, как только он будет проверен, поместить информацию об отгрузке и выставлении счета одаояремеино. С новой структурен» которую мы создали, когда наш заказ не может выть отгружен, мм можем вделать запись об атом. Мы можем также делать запись того, что клиент оплатил заказ. Обратите внимание, что в этом случае мы хотим моделировать только весь заказ, а не состояние отдельных компонент заказа. Решение этого типа ситуации в 4НФ будет иногда требовать диаграммы состояний для определения, каким образом достигауто состояние заказа. Например, может использоваться следующая диаграмма, чтобы описать процесс выполнения заказа со времени, когда он выл открыт, и до времени его завершения, (Отмена или изменение заказа или даже возврат заказа не будут рассматриваться жатом примере.)
Мы можем легко смоделировать этот вид простой диаграммы состояния с помощью одной дополнительной сущности: orderSiatusbreSie'eQ'agram orderStstuslypeStateDiagramia tromOrteiStetaeWeW (Ш • oOrd erStaiusTypeid (М) " ’-------•-------------------- I Г гост < : I I t I 1 orderStau slype crderStatusiypeM name Здесь orderStatusTypeState&iagram — срщносте "диаграмма состоянии заказа”; orderStatusTyp&StateDiagraald — идентификатор диаграммы состояний заказа; fromOrderStatusTypeld — идентификатор начального состояния заказа; toOrderStatuaT-ypeld —-ивентификавшр конечного состояния заказа; orderStatusType — сущность "тип состояния заказа": orderStatusTypeld — идентификатор типа состояния заказа; дате — название. Пусть мы имеем семь состояний в сущности orders ст.-. isType: OrderS tatusTyyeld (идентификатор типа состояния заказа) Name (название) 1 Рассмотрение заказа ; 2 Проверен 8 Отгрузка 4 Выставление счета 5 Выполнен б Оплата получена 7 Закрыт Чтобы определить нашу диаграмму состояний, нам нужны следующие экземпляры сущности orderStatusTypeStateMagram (на сей раз удалим указатели и включим для ясности названия): I’romOrderStatusTypeld (идентификатор начального состояния) Название taOrderS tatusTypeld (идентификатор конечного состояния) Название 1 Рассмотрение заказа 2 Проверен 2 Проверен а Отгрузка
fromOrderStatusTypcId (идентификатор начального состояния) Название toOrderStaiusTypeld (идентификатор конечного состояния) Название 2 Проверен 4 Выставление счета 8 Отгрузка 5 : Выполнив 4 Выставление счета в Оплата получена а Выполиев 7 За крыт в Оплата полутет 7 Закрыт Таким образом, мы можем видеть, находимся ли мы в конкретном состоянии, в каком состоянии мы были до этого и в каком состоянии мы можем оказаться затем. Это также позволяет нам определить поток наших конкретных бизнес-прав ил вместо жесткого кодирования набора фиксированных значений, В этом случае наше состояние может пройти несколько значений, и фактически может быть несколько состоянии в любое заданное время. Вы могли вы задокументировать бизнее-правила, точно очертив, как процесс работает в данной-ситуации, и фактически ато является обработкой исключений, которая требует большого количества времени, при атом до 80 процентов времени кодирования обычно тратится на исключения из иравил. Дополнительные нормальные формы В этом разделе мы кратко упомянем пару существующих дополнительных нормальных форм, хотя мы и не будем включать никаких примеров для них, поскольку они часто выглядят слишком заумными, и вообще реализуются только на стадаи логического моделирования. Однако они заслуживают, по крайней мере, упоминания ради законченности; темы. Пятая нормальная форма (5НФ) — в отличив от примеров, которые мы рассмотрели ранее, не всякие троичные отношения могут быть разбиты на две сущности, связанные с третьей. Если его возможно, то сущности находятся в 4НФ, Цель 5НФ состоит в том, чтобы гарантировать, что любые троичные отношения, которые все еще существуют у сущностей в 4НФ, могут быть расчленены на сущности и затем объединены друг с другом, чтобы сформировать первоначальную сущность. Если это нельзя сделать, то ' предполагается, что отношение содержит неистинную информацию. Следует сказать, что случаи, когда приходится иметь дело с 5НФ, очень специфичны и поэтому обычно игнорируются. Нормальная форме домен-ключ (НФДК) — сущность находится в НФДК, если каждое ограничение на сущность является логическим следствием определения ключей и доменов. Дональд Фатин был первым человеком, который придумал это формальное определение в 1981 г.
Рассмотрим, что каждый из этих терминов означает. О "Ключ" означает любой потенциальный ключ, который уникально определяет каждую строку в сущности, □ "Домен" означает любое ограничение ив вид даиных, которые могут быть размещены в столбце. Это могут также быть предписанные данные, использующие внешние ключи, если результирующая сущность определена должным образом и по крайней мере находится в 4НФ. □ "Ограничение" указывает любое правило работы с атрибутами (это включает правила редактирования, ограничения взаимосвязи, функциональную зависимость, и многопараметрическую зависимость, ио НЕ ограничения во времени). Такая форма считается "совершенной" нормальной формой, так как сущность не имеет аномалий, связанных с добавлением и удалением, и должна быть в НФДК. Денормализация Денормализация используется прежде всего, чтобы улучшить функционирование в случаях, когда перенормализованные структуры вызывают непроизводительные издержки процессора запросов в, в свою очередь, других процессов в SQL Server, или понизить сложность, чтобы сделать структуры более легкими для реализации. Как мы пытались отметить в этой главе, если удается аргументировать, что деиормалнзация до ЗНФ может упростить запреем, сокращая число необходимых соединений, то остается риск получения аномалии данных. Любой дополнительный код, написанный для борьбы с этими аномалиями, должен быть продублирован в каждом приложении, которое использует ВД, тем самым увеличивая вероятность человеческой ошибки. Необходимо сделать оценку, что сделать в данной ситуации — или выбрать несколько более медленное приложение (но со 100-процентной точностью), или более быстрое приложение, но с более низкой точностью. В книге утверждается, что в течение логического моделирования мы никогда не должны возвращаться от наших нормализованных структур, чтобы профилактически настроить реализацию наших приложений. Поскольку наша книга связана с OLTP-структурами ВД, наиболее важная часть нашего проектирования заключается в том, что мы должны быть уверены, что наша логическая модель представляет все сущности и атрибуты, которые будут находиться в окончательной БД, В течение, и наиболее важно, после процесса физического моделирования могут быть убедительные причины для денормал и а ации структуры или для улучшения функционирования, и л 1т для уменьшения сложности реализации, но ни одна из них не имеет отношения к логической модели. Мы всегда будем иметь меньшее количество проблем, если реализуем физически то, что является истинным логически, и поэтому я всегда сторонник подождать с денормализацией до физической стадии моделирования или, по крайней мере, пока не найдется неотразимая причина поступить таким образом (например, иекоторая часть нашей системы слишком плоха).
Учебный пример Рассмотрим повторно нашу логическую модель, которую зим оставили в конце прошлой главы. Если иы используем то, что изучали и этой гми, для нашего учебного примера, то мы найдем три примера нарушений 4НФ. Эти нарушения, оказывается, более часты, чем можно было бы подумать, и только когда вы продумаете каждый атрибут и его возможные значения, то начнете понимать, что нужно расширить вашу модель. Пример 1 — Account.BaianceDate Этот первый пример возникает, когда рассматриваем атрибутbalanceDaте (дата баланса) в сущности account (счет), которую мы первоначально разработали: : account_______________ ^tanlMWnwry^tFKHAia.l) '' 1 ассоийМ: Prim ary Key ; number: AlphaNumwicNumber (AM.2) i _ balanceOate: Date I_____________________________ Баланс по счету будет составляться часто, иначе любые пропущенные сделки или путаницы банка могут привести к тому, что мы случайно исчерпаем средства на счете. (Если даже и не так, он будет так же плох, как мой текущий счет, когда я вышел из колледжа!) Так как мы размещаем данные каждый раз, когда счет балансируется, атрибутЬаХапсеЮасе представляет по отношению к счету зависимость в несколькими значениями и должен быть перераспределен. Разбив эту зависимость, мы введем сущность accountReconcile (согласование счета), а также атрибуты reconcileDate (дата согласования) и periodEndDate (период до последней даты). Они учитывают тот факт, что может выть некоторое время-ожидания между временем получения счета и его фактическим - согласованием. Атрибут reconcileDate определяется как дата, когда выл физически получен баланс по счету. Кек последний штрих, жы можем добавить атрибут statsiaentld к сущности account Re cone 1 la, чтобы записывать любую информацию, которую банк посылает пользователю, чтобы сбалансировать счет. Будет также лучше иевользовать statementld как часть первичного ключа, так как он определяет согласование. С этими введенными корректировками наша логическая модель теперь будет выглядеть следующим образом: tank
Пример 2 — Payee. Addressld и Рау ее. Ph on eNumber!d Второй пример обычно связан с большинством предприятии, у которых имеется несколько почтовых адресов и несколько номеров телефона. Мы рассмотрели ио ранее в данной главе, когда- говорили о скрытых атрибутах, и нам нужно применить тот же самый процесс к нашему учебному примеру. Нала первоначальная модель содержала единственные сущности address и phoneNumber, кая показав© ниже; I I определяет расположения actress______________ address Id: PrmaryXey city tie! state: State Code zipCode: ZipCode Чтобы иметь возможность вводить несколько- номеров телефона, мы создаем сущность payeePhonaNumber (номер телефона получателя платежа) и добавляем сущность^ определяющую тип домеяа,— phoneNumbeilype (тип номера, телефона), чтобы иметь возможность выполнить классификацию этих номеров. Подобным способом мы создаем сущности payeeAddress (адрес иол у чате л я платежа) и scares sType (тип адреса). Наш скорректированный проект модели теперь выглядит так:
Обратите внимание, что теперь получатель платежа может иметь несколько номеров телефона и нисколько адресов в гибкой форме, которая позволяет нам определять новые типы адреса бее какого-либо дополнительного кодирования: так что у пользователя никогда не возникнет проблем, когда он или она должны будут ввести адрес. Однако если мы повнимательней посмотрим на сущностьадЛгезв, здесь все еще имеется проблема, связанная е тем, что атрибуты city (город) и state (штат) являются связанными (при этом не являются ключами), чем нарушают ЗНФ, находясь в одной сущности. Оба из них также связаны с атрибутом aipCode (почтовый индекс), поскольку если мы знаем почтовый индекс, то можем определить город и штат. Разбиение сущности address приводит нас к следующей измененной структуре:
Следующая стадия процесса должна определить точное отношение между сущностями city (город) и zipCode (почтовый индекс). Так как один почтовый индекс может охватить целый маленький город, но большой город может иметь несколько почтовых индексов, отношение имеет вид "многие ко многим". Создав сущности city, state и address, нам теперь нужно скомбинировать их атрибуты, чтобы можно было искать адреса, используя полные записи. Чтобы сделать это, создадим новую сущность zipCodeCityP.efereace (связь почтового индекса и города), которая объединяет атрибуты С у I t: (идентификатор города), Stateld (идентификатор штата) и zipCodeld (идентификатор почтового индекса) и включает новый атрибут (признак) РгхиагуП (первичный флаг). Первичные флаг позволит нам устанавливать, каной город в zipCode является "первичным", — атрибут, который почтовая служба продаст иаряду с ВД почтовых индексов и городов. Таким образом, наш пользователь может обычно получать все детали адреса, вводя в приложение только атрибут zipCcde. Организация этого в нашей модели даст: address Li ne______________________ add ressLin eld: Prim a у Key addressLine: AddressDescriptiveLine ^sortOrderld: SortOrderid f address______________ addressid: Pr'maryKey city: City state: StateCode apCode: ZipCode 1 имеет дополнительную информацию в state__________________ «Weld: PrimaryKey code: addressStateCode определяет почтовый региона ппрйлапяят расположение определяет расположение дрОойе zipCodeic: Prmarykey z<pCode:ZdCode определяет возможные города через zipCodeCity Reference /stateid: PrimaryKey (ВД « cityld: PrimayKey fFK) zipCode id: PrmaryKey (FK) ^primary Ft: Primaryfl city I 'state td: PrlmaryKey (ВД crtyld: Primary Key мп»: Ыап» определяет возможные почтовые индексы через Пример 3 — check. UsageType Атрибут usageType (тип использования) сначала предполагался как способ указания, для каких целей используется чек. На практике, однако, один чек может выть иаписан для многих различных целей, и, следовательао, мы имеем нарушение 4НФ, так как пользоватеш, не сможет поместить каждое использование чека в единственное значение. Чтобы устранить это, мы создаем сущность checkusage (проверка использования) и связанный с ней тип домена, подобно тому, что мы видели в предыдущем примере. Это дает нам следующую модель:
check является Обратите внимание, что сущность cbeckOsageType (тип использования чека) включает рекурсивное отношение, которое позволит пользователю создавать дереваuaageTypes (типы использования) для классификадан чека, так как мы теперь позволяем одному экземпляру checkUsageType принадлежать другому такому же экземпляру. Нвнрйжер, мы могли бы определить следующее дерево: Здесь Debit — дебет; Bill — счет.; Business Expense — расходы по сделке; Charity — благотворительный взнос; Utilities — коммунальные услужи; House Payment >— плата за жилище.
Теперь (используя алгоритм, которого мы коснемся в части книги, посвященной физической реализации, в главе 12) мы можем суммировать все счета, которые в нагнем дереве включают коммунальные услуги и плату за жилище, или все дебеты, которые включают расходы ио сделкам и благотворительные взносы. Я поместил этот. пример в книгу. чтобы показать, что а рвкурсивкыми отношениями не так уж сложна иметь дело. Основа алгоритма довольно проста, как только вы поймете это. Сначала вы получаете все строки, у которых нет предков (дебет), и спускаетесь по дереву на один уровень одновременно. Сначала это будет (счет, расходы по сделке и благотворительный взнос), а на следующем проходе — (коммунальные услуги и плата за жилище). На каждом проходе дерева мы просто суммируем величины и добавляем их к предыдущим уровням. Как уже упомянуто, мы сделаем очень простой, пример этой операции в главе 12. Теперь нам нужно завершить сущность с he ~ ku sage. Мы позволили пользователю определять многократные использования чека, но мы не разрешили распределять по этим использованиям сумму чека. Имеются два возможных решения данной проблемы: или размещать использование чека в виде процента, или в вида количества. В нашем решении мы выберем распределение в виде количества. Такое распределение дает пользователю способ дискретно назначить суммы на каждый тип использования. Если сумма чека изменится, распределение также должно быть изменено. Вели вы распределяете в виде процента, слишком легко забыть то, какие значения вы намеревались установить. Итак, мы завершаем ео следующим набором сущностей и новым атрибутом ellocetionAmount (сумма распределения): check___________________ account Id: PrimaryKey КапмсйопИ: PrimaryKey (RQ peyeeid: PrimaryKey Щ UMgBWe: Code signature: Blab имеет иелшъаоеание, определенноеs check Usage____________________ bankid; PrimaryKey (RQ account Id: PrimaryKey (FX) t’ansactionid: PrimaryKey (FK) checHfcag£$pel& P-itnujfey (FK) задает категорию чеке с помощ ью checWJwgelypa ------------ name: Name description: [‘description * j parentCneckUsageTyoaid: PrimaryKey iFKjt t L,_______________t является
К нашей документации мы должны будем добавить наше первое определенное бизнес-правило отношеютя между сущностям». Мы не будем допускать, чтобы сумма значений checkUsage +• allocationAjnount была больше, чем сумма сделки по чеку. Нужно также иметь другое правило, которое устанамивает, что сумма значений checkusage + allocationAmount никогда не должна выть меньше, чем сумма сделки по чеку. Это невозможно реализовать в SQL Server 2000, т должно быть отмечена для реализации промежуточного уровня или внешнего интерфейса, так кан это тип ограничения, которое SQL Server не поддерживает (ВД или ограничение конца сделки, где после сделки мы проверяем условие, чтобы удовтовериться, что все- сбалансировано). Мы теперь имеем большинство атрибутов, которые нужны для действительной реализации нашей системы контроля. Не будем увеличивать атрибуты модели, добавляя новую информацию в этот нормализованный пример. На .данной стадии это очень важно, поскольку мы будем иеоднввратио пересматривать модель. Это вдвойне важно в реальной практической реализации. Настало также время рассмотреть модель данным с клиентом, чтовы быть уверенным, что она точно моделирует их бизнес, В последующих главах мы иногда будем должны делать изменения в наших структурах, поскольку мы сделаем некоторый дальнейший обзор и начнем рассматривать, как пользователи будут взаимодействовать е данными. Заключительная логическая модель данных нашей системы следующая: Резюме В этой главе мы представили дополнительные критерии для нормализации наших БД помимо форм, представленных в предыдущей главе. Сейчас уместно быстро суммировать характер нормальных форм, которые мы выделили в этой и предыдущей главах:
Первая нормальная форма Все атрибуты должны быть элементарн ые, одно единственное значение на атрибут Вее экземпляры сущности должны содержать одно и то же число значений Все экземпляры сущности должны быть различны Вторая нормальная форма Сущность должна быть в 1НФ Все атрибуты должны определяться всем ключом, но не частью ключа Третья нормальная форма Сущность должна быть в 2 НФ Сущность находится в ЗНФ, если каждый иеключевон атрибут определяется атрибутами ключа Все атрибуты должны определяться ключом и только ключом Нормальная форма Бойеа-Кодаа Вее атрибуты полностью зависят от ключа; все атрибуты должны определяться ключом и только ключом Сущность находится в НФВК, если каждый детерминант является ключом Четвертая нормальная форма Сущность должна выть в НФБК У сущности должно быть не более одной зависимости с несколькими значениями Вея информация, которую мы изучили в этих двух главах, может вить сведена в одно предложение; Вее сущности должны быть отношениями и должны быть посвящены одной теие1 Формируя их для идентификации ЕДИНСТВЕННОГО человека, места, вещи или идеи, мы уменьшаем все аномалии корректировки хранимых нами данных. В этом случае, каждый раз, когда мы изменяем атрибут сущности, мы просто наменяем описание этой единственной сущности и не вызовем непредвиденное изменение в данных. Определение отношения было дано в главе S. Оно охватывает 1НФ, а единственная тема — все остальные формы. Процесс продвижения через все нормальные формы приведет нас к ситуации, когда потребуется очень небольшое специальное кодирование, чтобы сохранять правильные данные. В следующей главе мы рассмотрим проблемы, которые остались ва стадаи логического проектирования нашего проекта.
Завершение фазы логического проектирования Введение Подытожим, чего мы достигли к данному моменту: О им собрали ВСЮ и н форм я ци 5л • □ мы разобрались с реляционной теорией; □ мы узнали кое-что относительно UML; □ мы построили модель действий; □ мы разработали логическую модель нашей ВД, используя IDEFlX-методологию моделирования; □ мы нормализовали идти, данные. Теперь мы почти готовы начать нашу реализацию БД, Но имеется несколько проблем, которые следует выяснить прежде, чем мы начнем создавать таблицы и писать код. Так как мы все еще на стадии проектирования, гда весьма мало общаемся с пользователями, сейчас — лучшее время, чтобы получить ответы и* последние немногие вопросы. Что нужно делать, если логическая модель неточна, ведь это не физическая модель, и некоторые сказали бы, что это то, что отличает блестящий проект от нровто адекватного или даже нестандартного проекта. На СТОЙ стадии мы можем также рассмотреть организацию встречи со спонсором проекта для последней проверки, учли ли мы все требования. Рассмотрите также ппдгсспвку итогового документа для поддержания документации.
С точки зрения ВД мы теперь имеем очень хорошо структурированную модель данных, ясно представляем, кто является пользователями, и детализировали различные процессы, которые нужно реализовать. В этой главе мы вкратце рассмотрим задачи, которые нужно завершить до начала стадии реализации, и исследуем окончательные технические проблемы, которые являются важными для реализованной должным образом ВД: □ использование данных; □ размеры; □ план проекта; □ заключительный обзор документации. Каждая из них важна для процесса реализации ВД, которую мы начнем в следующей главе. Учет этих важных технических проблем теперь позволит вам вделать изменения, если вы кое-что пропустили, — это может случиться, поскольку никто не совершенен. Другой хорошей практикой при проектировании ВД (или любого другого типа приложения того же назначения) является привлечение других разработчиков, чтобы утвердить ваш проект, а также проверить на них ваши идеи. Проведите встречу с ними и объясните цели приложения, главные препятствия, известный бизнес-процесс и предложенный нет пряйст- ПрецстяилАнно нашей модели данных на простых чертежах аоан&лйт яятттим пирт-нерям легко понять и высказать критические замечания о работе, даже тем, кто не является архитектором ВД» Дайте им просмотреть все, оценить то, что выло сделано, и высказать свое мнение. Этот “процесс экспертаой оценки'* почти всегда (экономит вам время и энергию, так как любые предлагаемые приложения будут иметь аналогию с другими приложениями. Привлекая помощь других, вы можете поучиться тому, как они подходят к своим проектам. Используйте идеи и предложения, предлагаемые обзорах и критических материалах, чтобы улучшить вашу модель, и добавьте больше деталей. Наконец, независимо от того, насколько хорошо вы провели интервью ня начальной стадии, вам редко будет удаваться получить все необходимые‘данные От вавих клиентов. Выберите время, чтобы встретиться с клиентами и задать им еще несколько вопросов — это то время, когда вы должны выяснить вее, что еще не ясно. Вы также должны указать иа то, какие предположения были сделаны в течение логического проектирования* и каковы возможные последствия этого. Это — не так много, чтобы защитить тылы и оставить клиента е реалистическими ожиданиями проекта. Если проект не соответствует желаниям клиента, следует тут же критически разработать детали, даже если результатом будет возврат к началу процесса» и выявить все то, что выло неправильно истолковано, или даже отменить проект целиком. Истина будет всегда установлена, и именно сейчас для пользователя то время, когда он может обнаружить печальные детали того, кек мало вы понимаете в его бизнесе, а не после того, как ВД реализована и выясняется, что с ней нельзя работать. Использование данных Пока мы наметили, в каких данных мы нуждаемся и для чего это необходимо. Мы использовали копии сообщений клиенте, экранов и документов, чтобы попробовать подобрать данные, которые нам нужно будет хранить, ио окончательно не рассмотрели, для чего эти данные будут фактически использоваться. В этом разделе щ рассмотрим:
□ создание отчетов; □ использование данных и право доступа; □ взаимодействие с внешними системами; □ планы преобразования данных. Создание отчетов Создание отчетов — одна из наиболее важных частей СУВД, Почти каждая частица данных, которую мы собираемся моделировать, должна будет просматриваться кем-то в организации тем или иным способом или непосредственно, или, возможно, с использованием вычислений. При разработке многих проектов создание отчетов расематрнваетвя как дополнение, и часто проектирование и реализация отчетов откладываются до тех пор, пока проект не будет близок к завершению. Мы это, конечно, не защищаем,.но так бывает. Это бывает обычно с корпоративными/внутреняжми проектами, но случается также яс . конеалтинговыми/ввешяими проектами. Следующий график Гантта показывает типичную временную последовательность проектирования: Ns • aRggpfazH п 1 1 Реализация модуля 1 2/1/2001 02/02/2001 22 2 Реатйвция мс-Ду/.н 2 28/02/2001 2в'ОЗ/2СО1 ' 19 вв““и Реализация модуля 3 13-03/200* ytaWMpOl 24 -5 эзда/йда 13.'С4Ц0С1 1 1 5 Реализация отчетов 1в/04/2®1 18/04/гДОТ 3 Даже когда дая отчетов намечено разумное количество времени, они часто планируются на конец периода проектирования, так как мы действительно должны заиончигь разрабатывать ВД прежде, чем мы сможем начать формировать запросы и фактически организовывать отчеты. Тогда, если время проектирования затягивается, что может произойти по той или иной причине, время, отведенное для отчетов, может сократиться дажёеще в большей степени. Иногда, когда клиенты начинают понимать, что процесс проектирования завершается, а отчеты все еще ие разработаны, они начинают говорить и создавать проблемы, пока не убедятся, чя их отчеты у»гумядаст»тг»»г.я Однако з,а,:¥.е если клиенты « вдрмммют с криком, насколько для них важны отчеты, пользователи есегЭачрезвычайно заинтересованы в отчетах, поскольку это именно то, с помощью чего они управляют своим бизнесом. Отчеты — это то, как они получают информацию из ВД и видят ее, классифицируют, делят, планируют на основе ее и, что наиболее важно, — платят за них. В некоторых случаях отчеты — единственная частица проекта, которую пользователь будет видеть. Так что же нам девать? Прежде чем закончить стадию логического проектирования. очень, важно удостовериться, что мы поймаем То, что пользователь хочет получить от ВД. Даже если мм не можем сделать полный проект отчего», по крайней мере, вам нужно помять то, что нужно иметь, в получить общую структуру требовании.
Стратегия получения отчетов Имеются два различных типа отчета, о которыми мы должны иметь дело: □ Стандартные отчеты — отчеты, которые пользователь должен получать во время своей работы. Часто это — очень простые отчеты. Они Лычно составляют ядро потребностей пользователя. □ Специализированные отчеты — отчеты, которые позволяют пользователям идти дальше, давая им нечто большее, чем средняя информация, о которой они сами могут подумать. Это — две четко разлетающиеся вещи. Ставдартнмв отмети — отчеты, которые не требуют никаких специальных навыков для разработки, например, отображение баланса счета. Предполагается, что ваша система будет в состоянии формировать такие отчеты. Спедаализированные отчеты не столь очевидны— они иетояьзуют некоторые специальные навыки или понимание бизнес-процесса, например, объединение счетов многонациональной корпорации. На этой стадии проектирования чрезвычайно важно планировать оба типа отчетов. Стандартные отчеты Большинство СУБД имеет некоторый фиксированный набор требуемых отчетов, которые являются необходимыми для управления бизнесом. Они могут быть ежедневными, еженедельными или ежегодными отчетями, вс* предназначены -дая отображения ситуации, смеделщюванной в- СУБД, которую вы создали. Типииные примеры, которые мы встречаем иа каждом шагу, могут включать: □ Оценки Нельсона— имеется где-то БД, охватывающая вс® телевизионные передачи, которая контролирует просмотр передач людьми иа основе устройств, находящихся в их дамах , Ежедневно, ежечасно и еженедельно она (ВД) оценивает телевизионные передачи по числу пользователей, которые их смотрят. Эти тасла используются, чтобы определить, какие передачи оставить, а каки* закрыть. □ Продажи билетов на кинофильмы — каждую неделю в газетах печатается список десяти лучших кинофильмов недели. Эта информация поступает от типичной БД точно так же, как любая другая информация. Мейе® типичными примерами могли бы быть: Q Счет за коммунальные услуги — отчет о показаниях счетчика,- величина текущего начисления, предыдущего начисления, и, возможно, даже начисления за последний год. □ Отчет о движении — в местности, где я недавно жил (Виргиния Бич, штат Виргиния), были камеры и датчики, установленные на всем протяжении шоссе, которые обеспечивали Жителей пригородной зоны текущими отчетами о потоках даижения. Специализированные отчеты Кроме возможностей стаидартйых отчетов пользователи, с которыми вы разговариваете, будут иметь специальны* пожелания отображения данных, хранящихся в БД. Важно встретиться с клиентами, чтобы определить, как эти специальные требования формирования отчетов могут быть встречены. Пош»зователи часто имеют чрезмерные идеи в своих головах, ожидая, что получится, и их не всегда следует слушать.
Имейте в виду, что приемы, используемые для реализации отчета или хранения данных с этой целью, не важны во время интервью — хотя нужно выть осторожными, чтобы не зародить нереалистичные идеи в умах клиентов (вы должны создать ненаписанную спецификацию). Самая простая используемая стратегия состоит в том,-чтобы попросить ивяьзомтелей перечислить вопросы, которые они, очевидно, хотели вы задать относительно данных, которые вы будете хранить. Это служит двум очень важным целям. Мы сможем: □ понять, какие типы фактов их будут интересовать в будущем □ заметить, ве пропустили ли мы что-нибудь в нашем первоначаль ном проекте БД. Взяв наши примеры отчетоз, можно было бы задать слвдающие типы а» просев: □ Оценки Нельсона — пользователь, вероятно, хотел Вы знать кое-что етаосительно людей, которые просматривали программу ТВ, наблюдали ли они ее ягш. записывали иа плевку, и просматривали ли они ленту-после того, как была ааиисаиа программа. □ Продажи билетоз на кинофильмы -- возраст и категория людей, просматривающих кинофильм, когда они видели его, сколько было людей « средней группе зрителей, и сколько людей ушло, потому что они посчитали кинофильм унылым. □ Счет за коммунальныа.уеяуги — как иа каждодневные значения платежей действует погеда, выходные дни и другие ситуации. □ Отчет о движении — авализ тенденций того, сколько транспортных средств находится на дороге, как движение протекало в течение дня, влияние несчастных случаев и т. д. Имеется много проблем в разработке отчетов для пользователя. Фактически, сначала это может даже показаться невозможным. Однако классифицируя требования и оценивая их, вам будет проще сообщить, что возможно, а что — нет. Требования отчетов должны быть расположены по приоритетам и затем, принимая во внимание время и ресурсы, разбиты на необходимые и перспективна потребности. -Обратите внимание, что, цщуфректирошщиц, вы не должны бояться говорить нет, «тля выдвинутое требование фактически невозможно выполнить, Возьмем счет за коммунальные услуги, который мы ранее упомянули: □ Необходимые требования: чтение показаний счетчика, величина текущего начисления, предыдущего начисления, □ Перспективные требования: начисления за последний год. □ Невозможные требования: ежедневные значения (не могут быть выполнены из-за ограничений аналогового оборудования). Документ, документ и еще раз документ! Эта информация для кого-то будет зажна. Если вы — архитектор данных в большим коллективом, то ив сможете сами задать эти тины вопросов — это будет кто-то другой,. И никогда не скрывайте "невозможно** от членов команды проектировщиков. Это нельзя скрывать — именно потому, что мы имеем целые команды.
Прототипы отчетов Как только мы определили, какие отчеты являются необходимыми, нам нуггао построить прототипы того, как отчеты должны будут выглядеть, и какие двниые будут помещены s них. Почти каждый проект компьютерной системы использует прототипы экранов, которые входят в приложение, как копшо того, что клиент хочет и что мы ему поставим. Мы должны установить тот же вид и для самих отчетов. Когда наступает время проверки, имеются две точки зрения, с какими видами даиных проверять прототип отчета. Я приведу здесь для вас обе: Q Использование нереальных данных. Это не означает, что мы представляем клиенту причудливо нереалистичные данные, во берем типовые данные достаточно нереальными, чтобы избежать длинных обсуждений того, какова точная ситуация в отчете, которая никогда на самом деле не случится, хотя «о могло бы и быть, и т. д. Прототипы отчетов просто отражают возможные наборы данных для конкретной используемой в отчете ситуации, однако, не точную .ситуацию. Во многих случаях точные текущие данные клиента полностью не будут известны до волее поздних этапов процесса разработки. □ Использование реальных данных. В «ом случае прототин отчета формируется как можно ближе к окончательному отчету, это дает пол ьзователю возможность разобраться, как хорошо поняты данные, Использование действительных и подобных данных приближает пользователя к реальному проекту. Кроме того, «о обеспечивает раннюю обратную связь проектировщикам отчета относительно того, понимают ли оии требования и произведет ли их праек-т.желаемый- результат. Я обычно отношусь к первому лагерю, но это полностью зависит от ситуации, клиента, и, что наиболее важно, от того, насколько отчеты важны для процесса. В некоторых случаях они являются центральными, в других случаях овв ветомогатеяьны, Если мы должны разовраться с отчетом прежде, чем остальная часть системы будет успешно сформирована, то использование реалистических данных даст лучшие результаты. Определение использования данных и права доступа Понимание, какие данные будут использоваться, и кто будет использовать их-— важный фактор для завершения логической стадии. Прежде всего, нам нужно проверить, что наше понимание следующих моментов все еще актуально: □ Кто будет использовать данные, и что они будут делать с ними? Q Кому будут позволено что делать и с какими данными? Q Откуда они будут осуществлять доступ к даниым? - - □ Какие приложения будут использоваться для доступа к данным? □ Сколько пользователей будут иметь доступ к данным? □ Скольким из этих пользователей вудет нужно обращаться к данным одновременно?
Эти пункты должны быть рассмотрены на ранних стадиях анализа. Однако вяш доят выть уверенными, что ust полностью понимаем потребности в конкретных данных; рассматривая это, мы, возможио, обнаружим новые данные на стадии логического моделирования. Теперь перейдем к сложным структурным проблемам, вроде беаопаеноети и структуры доступа к данным, Безопасность Если клиент ке имеет заранее заданную структуру дла защиты своих данных от нежелательной корректировки/доступа, вопрос безопасности часто выдвигается на первый план даже по сравнению с формированием отчетов. Хотя это, возможно, может быть наименее важной частью проекта для архитектора данных, но, конечно, важно для администратора БД, так что данный вопрос нельзя просто игнорировать. Когда это связано е отчетами, безопасность становится ультра-важной проблемой. Be важность зависит от информации, содержащейся в отчетах, в которую клиент может использовать, но редко в организации будут желать, чтобы каждый пользователь имел доступ ко всем данным. Хороший подход для проектировщика — установить "таблицу классификации отчетов" и получить от спонсора проекта инструкции по классификации каждого отчета, и какие пользователи или группы пользователей будут иметь доступ к каждому отчету. Включение информации о безоимяоети в описания действий каждого исполнителя в системе — очень полезная техника. Безопасность подразумевается каждым процессом на диаграмме действий, так как, если действия включают каждый возможный отпет и каждый возможный процесс, это будет означать, что когда действие не связано с пользователем, то он не может исполнять некоторую функцию. Например, возьмем наш пример действий иа текста, в главе 5:
Каждое из действий будет модулем системы, так что автор должен иметь доступ ко всем данным, используемым в модуле, который помечен- названием "Написание книги". Никто другой не имеет доступа К этим данным, пока они используются в отдельном модуле следующим обрезом: Теперь рассмотрим, что произойдет, если мы добавим отчет, названный "Отчет о книге". Для этого мы создадим действие и отметим, что оно содержит очень неашого информации типа имени, описания, рисунка, служебных описаний и денежной информации: Автор может рассматривать отчет, но его рассмотрение должно быть ограничено и не включать такие вещи, например, как детальная денежная информация. В структуры определения исполнителей мы могли бы включить: Имя Автор Описание Исполнитель, который пишет текст книги Замечания по безопасности Может просматривать только упрощенную версию отчета о книге, который содержит служебную информацию Конечно, вы будете, вероятно, использовать некоторый вид программных средств, который может работать с такой документацией или фиксирует используемые ноля. При использовании "ответа о книге" мы будем фиксировать версии и ограничения безопасности, когда он будет использоваться. Известные ограничения структуры Мы рассмотрим в книге определение окончательной структуры позже, но я представлю его здесь, поскольку на этой стадии процесса проектирования, вероятно, это последний шанс, когда вы можете сиросить клиента относительно фактических требований, которые будут наложены на ВД. Вот о чем нам нужно узнать; □ Тип связи — как клиенты будут фактически осуществлять доступ к данным: через Web-страницы, через локальные приложения? □ Проблемы программирования — с каким языком программирования они связаны? Или мы можем использовать все, что захотим?
□ Вероятное требование к системе — минимальное и максимальное число пользователей системы и требуемое время функционирования. □ Ограничения аппаратных средств — это могло быть по политическим или денежным соображениям, но представьте, что мы полегаем, что пользователь собирается потратить на оборудование миллион долларов, а он рассчитывал на пять тысяч. □ Физические ограничения — проблемы сетей подобны недостатку полосы пропускания, или даже лучше, отсутствию нужного помещения, так что приходится разместить некоторое оборудование в защищенном шкафу отдельно от сервера. Мы должны получить некоторую возможность регулировать функционирование (как быстро работает ВД) и масштабируемость (как изменяется функционирование при увеличении числа пользователей) прежде, чем мы перейдем к физической реализации. Взаимодействие с внешними системами Когда я говорю относительно "внешних систем", я имею в виду другие ВД, которые не являются соответствующей частью нашей ВД, но с которыми нам нужно взаимодействовать. Общие примеры включают любой имеющийся в наличия механизм (система людских ресурсов, система платежных ведомостей и т. д.), который есть у организации и который нужно связать е нашей ВД. Главная проблема е внешними системами состоит и том, что по тем или иным причинам некоторые были плохо разработаны. Немного имеющихся в наличии систем, все еще использующихся сегодня, были разработаны как системы на универсальных ВВМ и в спешке перенесены на реляционную СУБД, потому что ато было модно, при малом понимании реляционного программирования. Примеры в этом разделе основа»» на реаяьны* ситуациях в мире, так что, даже если они и покажутся нереальными, но все-таки копируют конкретные проблемы в реальных процессах. Они включены главным образом кая шокирующие примеры, но и для того, чтобы дать представление, что может произойти, если мы слишком поздно займемся внешними системами, е которыми нужно установить взаимодействие. Пример 1 — Проблематичная система планирования ресурса предприятия Приложение, связанное с покупкой и спросом, с которым я работал, имело почти тысячу таблиц, но только одну таблицу доменов, которая служила в качестве доменои для всех таблиц системы. Вместо отдельных таблиц, смоделированных примерно таким образом: арвсШоТвЫеОопмт ареЩйТаЫеОотатИ сате descopfian <Расшире«,ыв а^р<9уты 5pecifpTab>Oomain> ' Збвсь sped ficTableDontain — сущность "еявциалышя таблица йжвиов”; spedficTableDcmainld — идентификатор элементов специальной таблицы доменов; name — название; descxipt-ion — описание.
... в каждом месте, где они нужны, например, тип оплаты. тип ресурса, тип закупки, и т. д„ некоторые программисты предпочитают реализовать единую таблицу подобно еледаюшей: реаеисШМЭрта1п_________________ genericBbHJanwWci reteeRAble vaiue name d ascription <Ржвфе*в«лвфибу1ыяи twO/ww* i> «Рившиоегвьв атри&пы для тавящы 2> :<РИ»а^ИЯлв1йВ¥ШВй,1ЙЖав**й« Здесь genericTableDmaain — сущность "таблица генерации доменов”; generlcTable-Domalnld— идентификатор элементов таблицы генерации доменов; eefersToTatole — ссылка на тобжиф; value — значение; паМ имя: description—описание. Идея программиста, должно выть, состояла в том, что sjfc использовании единственно! таблицы, чтобы выполнить много операций, ему/ей потребуется только один редактор, чтобы управлять всеми значениями доменов. К сожалению, имеется весьма мало задач при использовании SQL с данными в такой реализации. Мы игнорируем очевидные трудности реализации (подобные невозможности использовать декларативную ссылочную целостность, так как специализированны® таблицы требуют ключевых величин, которые невозможны в любой СУРВД) и рассмотрим проблемы преобразования данных. Было более вос-ьмидееяти различных зиачеетй в столбце refersToTable таблицы доменов. Так как этот метод создания -таблицы домена выл нест андартным, никакие средства преобразования данных не поддерживали этот тип запроса, а новая система, которую мы разрабатывали, конечно, не повторяла этот проект. Очень много запросов пришлось вручную закодировать, чтобы извлечь данные. Пример 2 — Другой пример "Как не надо делать" Следующий пример возник с программным продуктом третьего липа, с которым я имея "удовольствие" взаимодействовать, и это позволяет взглянуть на предыдущую ситуацию как на весьма легкую. Я реализовывал систему, которая должна была взаимодействовать е одной из наших бизнес-программу которую мы использовали долгое время ж не могли яяменить. Система хранила свои данные в SQL Server, но была реалнзовяйа таким образом, чтобы препятствовать взаимодействию е другими системами. ВД использовала структуры, которые маскировали названия столбцов таким образом, что вы должны были пройти их интерфейсы, чтобы разгадать, какая таблица находится в ней, Например, следующая таблица показывает метаданные, которые используются, чтобы определить поля таблицы: Фактическое название таблицы Фактическое название столбца Логическое имя поля (имя поля, которое мы должны знать ДЛЯ ПОЛЯ,! Тип Возможность значения NULL TABLE1 COLUMN1 Key Id varchar(30 J Нет TABLE1 COLUMN2 CreatedBy sysname Да
Фактическое название таблицы Фактическое название столбца Логическое имя поля (имя шля, которое мы должны зн ать для поля) Тип Возможность значения NULL TABLE 1 COLUMNS CreateDate int Нет TABLE1 COLUMNS ModifyPate dstetime Нет : ТАШ.Е1 ’ C0LUMN6 Fieldl varchart10) Д« TABLE1 COLUMN? FieldZ varchar(10) Да TABLE1 COLUMNS Field3 varchar(15) Да TABLE 1 COLUMN» Fields . varchar(30) Да Проектировщики продукта сделали это, чтобы скрыть реализацию от пользователей. Как механизм зашиты от понимания структур конкурентами вто работает поразительно. Однако построение интерфейса для восстановления данных из этих таблиц является сущим кошмаром, требуя отчеты типа» select COLUMN" as fieldl, CCLUMM8 as fields, COUWJ as fields, CO1AJKK10 as fieidi,. COLUMHU aa fields, COLUMKn as fieldlJ TABLET C:?L'.’KL'-.' - ‘ «значение пом>’ Выполнение преобразований данных путам создания операторов для изменения данных в таблицах из нашей новой системы было отвратительной и отнимающей много времени задачей. Пример 3 — Системы, страдающие от денормализации Одна из наиболее частых проблем,- е которой вы будете стал киваться ири работе е другими системами денормализация. Эти системы могут быть денормализованныки по многим причинам. Возможно, проектировщик ВД не был очень опытен или, возможно. эта система — наследство времени, когда технология яе поддерживала полную нормализацию. Нисколько яе удивительно видеть ВД с одной, двумя, или. возможно, тремя тавлй^жи, когда требуется пятьдесят, чтобы должным образом конкретизировать решение. Например, мы знаем/что^тавлйца, которая моделирует человека, должна иметь только(Ирйбуты, которые определенно описывают человека, но плохо разработанная «гаЯднци, может - - напоминать следующее:
person_____________________ sadalSecor; tyN umber frstNarne fast Name homeAdddressUnei horreAd dress Lines homeCity horn estate homeZipCode off iceAd d ressLi n в 1 offceZipCode soouseName dateOfBrth homeTeiephoneNumber offlceStephcneNumber computerUserName computerpassword printsrPassword salesToialQiiarterlofYear2000 salesWaiQuaitertofWarMOO ^es»WQuatetofifcar2000 salesTbialQuarter4ofYsar2000 chMNamel chidNameZ < и »то долго продолжается подобным образом> Здесь person — сущность "человек"; soaialSecurityNuaber — номер социального страхования; firstNama — имя; lastUamo — фамилия; haaeAddrssshine! — первая строка домашнего адреса; homeAddressline2 — вторая строка домашнего адреса; homed су — город проживания; haoeState — штат проживания; hoaeZipCode — домашний почтовый индекс; officeAddresshiael — первая строка адреса офиса; officeZipCode — почтовый индекс офиса; spouseName — имя супруга (супруги); dateOfBirth — дата рождения; hameTeiephon-eNuiBber— номер домашнего телефона; ofTioeTelephoneNumber — номер телефона офиса; ааяриСегияегЫалв — название компьютера пользователя; coaputexPassword — пароль компьютера; pr in terBas sword — пароль принтера; salesTotalQuarterlofYear2G0Q = общие продажи за 1-й квартал 2000 г.; SalesTotalQuarter2o£Year2Q00, — общие продажи за 2-й квартал 2000 г.; salesTotalQaarter3ofYear2000 — общие продажи за 3-й квартал 2000 г; salesTotalQuarter4ofyear2000 — общие продажи за 4~й квартал 2000 г,; childNamel — имя первого ребенка; childNaoeZ — имя вторшо ребенка. К счастью,, вы не встретитесь с таблицей, так смехотворно даиармализованной, как эта, но даже наименьший уровень денормалнзадии в исходной системе будет требовать сложных запросов, которые будут загружать данные в наши таблицы. Дополнительные проблемы, возникающие при взаимодействии с системами третьих лид Одна из основных проблем при взаимодействии с внешними системами третьих лиц — связывание. Причина в том, что мы моделировали всю нашу ВД, ве заботясь о внешних связях, в то время как для большинства случаев важно, чтобы наши системы свободно связывались е системами, которые находятся вне нашего контроля. Обычно хорошая идея 21в
импортирования данных из внешних таблиц в структуры, которые мы разработали, — это помещать данные в набор промежуточных таблиц, которые будут работать как импортирующая прослойка независимо от внешней используемой системы. Другая техника объединения с внешними системами, которые содержат подобные, но различные структуры данных, использует построение представлений данных, содержащихся во внешних системах. Предположим, что вы имеете-систему хранения запасов с ядром, представляющим совой таблицу, которая хранит данные в специфической структуре, и систему кассового контроля, которая хранит их в несколько отличающемся формате, но из которой нам нужен доступ к той же самой информации. Абстрактный елей, созданный с этой целые, позволяет модифицировать основную систему без необходимости изменять внешнюю систему кассового контроля. Другая серьезная причина идентификации всех внешних систем, с которыми нам нужно взаимодействовать, — это то, что мы не хотим начать размещать данные с информацией, которая уже существует и доступна нам. Нет никакого смысла в создании новой таблицы служащих, если внешняя система, имеет таблицу служащих, которую мы можем использовать. Планы преобразования данных Нужно также учесть тот факт, что мы, возможно, заменяем систему, которая используется в настоящее время, даже если это система на бумажных носителях. В то время как преобразование данных — не столь уж логическая проблема проекта, нам все-таки нужен эскиз планов преобразования. Много проектов терпят неудачу из-за недостатка планов получения данных из существующей системы. Если ваша ВД реализована иа SQL Server 2000 должным образом, ио будет, вероятно, намного лучше, чем предыдущая система, но люди будут яе слишком счастливы, если потребуются огромные усилия для перехода от старого к новому. Еще хуже, если вы к тому же измените правила работы с данными, которые ве позволят использовать данные предыдущей системы, особенно когда они уникальны. Часто большинство старых данных не будет вписываться в новую ВД без некоторого их приглаживания из-sa измененного программирования и/или проекта, используемого в старой системе. Планирование требуемых объемов Требуемый объем определяется как "непосредственное или косвенное иамереиие объема" и связан с определением, как будут расти ваши таблицы и БД. Этот- тип информации будет очень полезен, когда мы начнем физическое моделирование. OfcpiHo вы получаете некоторые понятия относительно размера таблиц, которые вы разрабатываете, из бесед с клиентом и рассмотрения предыдущих систем, которые вы заменяете. Требуемый объем — в большей мере относится к вопросам физического моделировании, и мы рассмотрим это должным образом позже в книге. Однако лучше начать подбирать некоторую основную информацию, чтобы ш могли оценить размер БД и ее рост. Имеется несколько .основных частей информации, которую вы можете начать собирать в этот момент процесса проектирования: □ Средняя длина атрибутов данных (включая текст ж BLOB-данные). □ Процент даиных, который будет заполнен для каждого атрибута.
□ Сколько данных будет, вероятно, в таблице первоначально. □ Число строк в таблице и как это число будет расти, Q Сколь долгой ожидается жизнь проекта. Например, в нашей ВД заказчика мы могли вы иметь следующую -таблицу значений, которые помогут оценить размеры ВД: Название таблицы Начальные строки Увеличение строк в месяц Максимальное число «прок customerType (тип заказчика) 4 0 20 customer (заказчик) 0 30 3000 order (заказ) 0 1000 Не ограничено В этом случае мы говорим, что начнем с 1 строи в таблицеcustomsгТуре (тип заказчика) и не ожидаем большого роста, поэтому планируем максимум 20 строк, В табливесизЬстег (заказчик) мы начнем без данных, но ожидаем добавлять 30 отрок в месяц, с ожидаемым потолком в 3000 строк. В таблице order (заказ) мы надеемся, что будем продолжать добавлять 1000 строк каждый месяц. Наличие этого вида информации в начале планирования аппаратных средств ЭВМ будет чрезвычайно полезно. План проекта В этот момент я должен упомянуть о необходимости плана проекта. Каждый шаг по планированию будет относительно прост, если вы прошли через процесс создания ВД десять или двадцать раз. Должны быть включены следующие элементы информации: □ Какие задачи должны быть выполнены. □ Какие задачи должны быть закончены в первую очередь. □ Достигнутые этапы, которые должен расемотретВщоЛзоаадель. . : □ Сколько времени займет каждая задача. Задача создания плана проекта непосредственно связана о проектом упрмлевия командой проектировщиков и но существу находится за границами возможностей этой книги. Дополнительная информация может выть найдена в Pratesgtonal VB6 Project Management, Jake Sturm (Wrox Frees, ISBN 1-861002-93-9). Окончательный обзор документации В течение процесса проектирования вашей системы мы накопили тонны документации. Окончательный обзор означает точно то, что он означает. Вся документация должна быть пролистана, исправлена и синхронизирована. Этот шаг часто пропускается или существенно сокращается по сравнению с планом проекта, когда нехватаеу времени, ио дело в том, что
даже при хорошей документации могут быть большие различая. В процессе проектирования часто нарушается синхронизм документации, особенно когда вовлечена большая команда проактяртнииков. : - Как пример, я был архитектором дайн ы к проекта для предприятия ^ф^Ьтыважицей промышленности среднего раэаифа, Суть проекта заключалась в размещении измерений образцов изделий, которые они производили, для целей управления качеством. Мы собрали информацию относительно того, что они делали, что ояи хотели делать и как они будут использовать данные. Мы определил! их потребности, сымпровизировали экраны, и разработали ВД для решения их проблем. Проблема состояла в том, что в то время, как мы поняли’ основные принципы их потребностей в формировании отчетов, ин не понимали все, что требовалось: □ Формирование отчетов было достаточно сложным из-за запутанных вычислений (один из запросов содержал более 260 строк кода), так что когда мы смоделировали тестовый запрос и получили результаты, мы не могли точно проверить, как они будут использовать данные. Требовалось вол» пятидесяти старых измерений, чтобы вычислить новое значение, и что произойдет, ее» кто-то вернется и изменит одно из тех старых нокавателей ...? Они- хотели иметь графики с последней сотней - вычисленных значений, так что вычислениям,валету яе была альтернативы. Мы только не могли понять, как эти расчетные значения можно использовать или как сделать их более ценными для персонала. □ Далее, мы яе могли понять организационную структуру компании, с которой мы работали. Мы имели дело с двумя людьми, которые управляли проектом, и они казались достаточно опытными, но впечатление, к сожалению, оказалось обманчивым. Во время стадии проектирования основной партнер пользователя все еще обучался, К концу шестимесячного проекта они, наконец, поняли точные потребности для системы, которую мы разрабатывали, но это было слишком поздно по времени. Q Третья проблема была самой плохой. Мы не сумели должным образом рассмотреть нашу документацию и удостовериться, что все, что было согласовано, было записано. Произошло это потому, что был контракт на время исполнения .и доступные материалы. К сожалению, нужно было дать им оценку предстоящих затрат. Мы получили величину, неправильную, по крайней мере, на 200%, и проект потребовал для завершения в три раза больше затрат. В результате мы потеряли последующую работу, которая досталась другой коясаптингввЛфир|И. '' ' ’--и. Важная мораль згой истории — то, что-всё зги три проблемы испарились бы, если Вы мы полностью рассмотрели документацию и заставили клиентов отказаться от йретената-. Вели бы весь иаш план был придирчиво сформирован, даполнитвданое время не потребовалось бы, — это было fe предсказано. Вместо этого мы получили неприятность, потому что у нас были другие планы работы. Не всегда возможно документировать каждую деталь системы, которую вы намереваетесь создать. Однако чрезвычайно важно на пая стадии рассмотреть документацию, удостовериться в том, что все определенное вами, может деЛявительяо.быта_создаяо иато время, которое вы предложили, и, что требования к вам не слишком серьезны, чтобы нельзя было завершить работу. Независимо от ти па проекта, все это — корпорати вность, время и материалы, или установленная цена — всегда будет иметь для вас смысл, чтобы защитить себя от некачественной документации.
Дальнейшие требования Как заключительный шаг, мы должны задокументировать любые последующие требования, которые могут возникнуть у клиента в отношении системы, которую мы создаем. Ваша работа как проектировщи ка — постараться определить дальнейшие платах проехжроваяия, и какие могут возникнуть требования -в результате этого. Везде, где это возможно, проект должен выть изменен, чтобы их учесть, пока это существенно не увеличивает затраты. Вы будете выглядеть довольно глупа (и к тому же намного более несостоятельным), если через шесть месяцев после завершения проекта клиент попросит сделать, как он считает, тривиальные изменения, а вы должны будете сообщить ему, что необходимо шесть месяцев, чтобы их реализовать, потому что вам потребуется изменять 50 таблиц, особенно, когда вы знали, что это могло бы быть возможным требованием с начала проектирования. Небольшое предварительное цианирование могло вы означать благоприятный сдвиг во времен и всего в две недели, счастливого заказчика, и возможный будущий бизнес. Очевидно, что вы не должны загубить себя,: пытаясь пересмотреть каждую потребность или прихоть клиента, — вы должны отдавать себе отчет в этом. Будьте честны с клиентом. "Вели мы затратим дополнительные дна дни, добавляя X, это будет сделать намного легче, чем добавить Y, если его потребуется в будущем". Веля клиент говорит, "Ха! Нет это никогда не потребуется", тогда все ясно, ио следует попробовать получить это в письменной форме! Вудьте действенными. Как профессиональный проектировщик, вы далзтаы думать за пределами простых границ, установленных клиентом. Предаоложение-о возможных будущих улучшениях и шинирование их показывает, что вы понимаете бизнес клиента и редко вредите банковскому балансу. Учебный пример Когда мы последний раз покинули учебный пример, мы создали модель наших данных, создали модель действий и были готовы начать создание БД. Но это еще не все — у нас все еще нет некоторых чаете! информации, которую нужно совать. Использование данных В нашей диаграмме действий мы определили двух различных -пользователей системы: основного пользователя счета (тратящего средства) и клерка бухгалтерского учета (регистратора). Снова воаьмеж интервью у клиента, чтобы попытаться определить, какие действия могли бы быть выполнены над данными,- При этих обсуждениях мы выясняем, что нужно добавить несколько дополнительных действий и дополнительного исполнителя. Исполнитель, которого прежде всего можно определить, — пользователь-менеджер, пользователь, который просматривает ретюме счетов и действия других пользователей счета. Мы определили три новых действия: итоговые отчеты, отчёты пользователя н предупреждения о счетах. Обсудим точно, какие потребуютсяотчеты в следующих подразделах, но предупреждения о счетах определены кая сообщения, посылаемые пмыомтеля, чтобы сообщить ему, что счет трс-бус-т некоторого внимания, типа общего превышения счета.'

Отчеты 'Karpfi. мы спрашиваем пользователей, какие отчеты они должны иметь, то получаем следующий список: □ Резюме счета — просто категоризированный список сделок, основанных на поле checkusage (использование чека). Этот отчет должен позволить пользователю определить уровень показаинь» подкатегории. Например, они могли вы просто запросить обо всех дебетах и кредитах, или другого уровня, где они могут видеть основную классификацию — все счета, расходы по сделкам и сделанные влаготворительные взносы, как мы показали в примере таВлицы checWsageType (тип использования чека) в предыдущей главе, □ А|,-тивн:х;ть пользователя — список всех сделок, евяватшх с данным пользователем, или просто егрупшфоваиныв на пользователям. Также нужно иметь возможность показать любые сделки, не связанные с пользователем, который фактически потратил деньги, □ Балане счета — список всех счетов и балансов. Дальнейшее обсуждение приводит к тому, что пользователи упомянули о специализированных отчетах: □ Тенденции расходов — очень полезный отчет, который довольно легко программировать, но это обычно не требуется; этот отчет дает тевдевдиг того, кода идут даньги, в графической. более легкой для понимания манере, чем отчет-резюме по счету. □ Оценка последующего баланса — хотелось бы ваять расход в течение предыдущих месяцев и экстраполировать его, чтобы получить оценку возможных затрат в будущих месяцах. Это было вы похоже иа бюджет. Такой отчет также был бы полезен при формировании бюджетов. □ Автоматическая выписка чека — в будущет хотелось бы игегь средство автоматической оплаты счет, в том числе иметь автоматическую оидату е некоторыми интеллектуальными включениями для ситуаций, где требуемая сумма недоступна для счета и т. д. Пользователи также хотели вы формировать витрины, данных, основанные на имеющихся, данных, чтобы получать мнмогруааавые отчеты, основанные на получателе платежа, местоположении получателя платежа, откуда поступают деньги, какое было количество денег и т . д. Это, конечно, сверх и вне наших текущих возможностей, но очень интересно об зтом знать. Обратите внимание, что мы обнаружили, по крайней мере, один новый элемент данных и новую таблицу для нашей ВД в отчете о деятельности пользователя. Так что мы добавляем таблицу user (пользователь) я связываем ее с чекам и депозитом подобно следующему:
Здесь обозначения те же, что и на соответствующих предыдущих рисунках. - - - Это позволит мам отслеживать пользователя, который выписывает чек. Кстати, это — другой пример того, почему мы должны пройти полный процесс проектирования, ие пропуская никаких шагов, ви одйого неперевернутого камня. Прототип Отчета Теперь мы создадим прототип отчета из тех, которые мы внесли в список в предыдущем разделе. Чтобы сэкономить место, в этом учебком примере я выбрал для долей вялюстрации только один отчет — отчет-резюме счета. Как вы можете видеть, мы добавили простое резюме к основным требованиям, которые нам выдали. Тогда мы имеем каждую из полученных в итоге групп типов использования чеков, и суммы, перечисленные во втором столбце. Мы создадим прототипы для- каждого из трех требуемых отчетов. Будем заниматься здесь именно ими, поскольку они должны быть хорошо обработаны вашими разработанным;; структурами. Специализированными отчетами, которые требовали пользователи, можно было бы заняться в следующей итерации системы, которая последует в нашей работе.
Взаимодействие с внешними системами Единственная внешняя система, в которой мы должны будем иметь дело, принадлежит бан ку. Как вы можете вспомнить из исходных документов интервью, мы имеем формат автоматического регистра банка, повторенного здесь: В бумажной версии они имеют дополнительные итоговые типы значений, которые мы очевидно не получи из электронной версии. Мы будем, наверное, желать сделать поля нашего отчета, заполненные нулевыми значениями, или, возможно, заполнить их из итоговых элементов. Мы также должны лобаяить нескотькп к нашей модели данных для statement Items (элементы отчета) и обращаем внимание, что даже не рассматриваем, как элемент будет еотяааааах, Так что мы корректируем нашу модель данных следующим образом: б account
Обратите внимание, что мы добавили четыре атрибута к statementItem (элемент отчета — см. эту сущность в-гл*-в) для каждого на полей в загружаемых пунктах отчета байка. Мы также обращаем внимание, что для использования этих пунктов для баланса нужно связать каждый пункт, используемый для баланса, е пунктом в регистре. Так же как и в реальной жизни, мы обнаруживаем новые веши относительно наших представлений, поскольку продвигаемся вперед. Обратите внимание, что мы построили твердую основу в наших предыдущих главах и все, что теперь собираемся делать, — заняться вылизыванием, залатыванием дыр, которые мы вначале не замечали. На этом этапе мы должны попытаться избежать "паралича анализа". Постоянно анализируя, непрерывно опрашивая клиентов и внося все больше и больше изменений в проект, мы никогда ив перейдем к стадии реализации. В некоторый момент под проектом должна быть поставлена точка, даже если он и не совершенен на 100 процентов. С этими недостатками придется иметь дело и а сталии реализации. План преобразования данных В нашей системе клиент решил начать с нуля с регистром чека; следовательно, нам не нужно осуществлять никаких преобразований данных. Мы просто начнем счет с кредита или дебета для начального баланса. Это — самый простой тип плана преобразования данных, то есть, Ничего не нужно. Поскольку мы заменяем бумажную систему, то если вы клиенты хотели сделать преобразование данных, им потребовалось бы ввести все данные вручную, Часто вто невозможно, поскольку слишком дорого и проблематично. Требуемые объемы Для определения требуемых объемов мы строим простую таблицу, содержащую детали всех других таблиц нашей БД, с ожидаемым числом строк, которые таблицы будут . первоначально содержать. Лучший способ оценки этого состоит в том, чтобы взять важные таблицы системы, как показано в нашей модели данных из главы 7, и начать с них. Будем считать, что хорошей отправной точкой для наших оценок Выла бы таблицаагапдасс!от (сделка). Тогда мы просто спрашиваем у полы&ватвля относительно каждой таблицы или группы таблиц, и экстраполируем это на некоторые другие таблицы, ос-сВвво те, которые мы сами добавили. Обратите внимание, что мы также йудем должны соврать некоторую статистику по столбцам. Сделаем весьма немного решений относительно того, как реализованы столбцы, а пользоватешгмогут помочь в оценке их значений. Вы можете также обратиться к существующей системе как к руководству. Возьмем сначала таблицу сделок, В нашем случае пользователи решили начинать, с нуля, без фактических сделок. Они ожидают иметь около 300 сделок в месяц, из которых 245 являются чеками, Б — депозитами, в 60 — прямыми изъятиями. гае
Отсюда мы получаем опенки числа различных получателей платежа, с которых можно начать, и сколько ожидается добавлять. Овмчно мы предполагаем остальную часть количества параметров» зависящей от эти значений. Например. иа оценки 245 чеков в месяц мы предполагаем, что они имею® 100 получателей платежа, которым зги чеки предназначены, и, вероятно, будет добавлено 30 новых получателей платежа в месяц. Очевидно, чем вольшую исходкую информацию вы получаете от клиентов, тем более достоверны будут эти оценки. Мы завершим следующей таблицей оценок,, которую следует рассмотреть с клиентом. - Название таблицы Начальное число строк Увеличение числа строк в месяц Максимальное число строк account (счет) 1 0 20 accountReconclie 0 1 36 (согласование счета) address (адрес) 50 30 600 addressLine (строка адреса) 55 35 700 addressType (тип адреса) 5 0 10 bank (байк) 1 0 5 check (чек) 0 845 10000 checkRegister (регистр чека) 0 1 Зв checkUsage (использование чека) 75 400 12000 Check'’>3 де Туре 20 0 « (тип использования чека) city (город) 450 20 25000 deposit (депозит) 0 5 4000 directwithdrawal 0 50 1000 (прямое изъятие) : payee (получатель платежа) 100 во 300 ; PayeeAddress 100 30 600 j (адрес получателя платежа) i payeePhoneNuraber (номер 100 30 600 f телефона получателя платежа) phoneNumber (номер телефона) 50 30 600 . FhoneNumberlype 5 0 10 : (тип номера телефона) । state (штат) 60 0 го : statement (отчет) 0 1 Зв ' statementltea (элемент отчета) 0 300 15000 ; transaction (сделка) 0 300 15000 user (пользователь) 10 2 50 : zipCode (почтовый индекс) 1000 10 99099
Вот описание содержания таблицы: □ Начальное число строк — сколько строк, как мы ожидаем, будет добавлено к таблице, когда система начинает функционировать (нуль умазывает, что нет никаких строк). □ Увеличение числа строк в месяц — число строк, иа которое, как мы ожидаем, увеличится таблица (нуль означает отсутствие роста), □ Максимальное число строк — грубая оценка максимального числа возможных строк. План проектирования Сделаем некоторые оценки относительно длительности процесса проектирования в виде плана проектирования. Например, чтобы закончить задачу создания этой БД, мы могли бы воспользоваться диаграммой Гантта, наподобие: ! ’ I S’' Г 1" 1 I -Г'| 1 ; - Г Г Н Г У: о даяюл JP.C-Cj- S - , 2 «ЙйОЙу iVICyO!T 19 3 Создание жсда е-АС-ад ПиШ , 24 - - — уТ-: Л СШМ^ CMrSB : у ШВ к жат ilUif — 30 Нам нужно конкретизировать каждую задачу, например: Физическое моделирование: Выбор типов данных — 1 день Проектирование оптимистических механизмов блокировки — 2 дня Или кое-что вроде этого. Количество времени для задач будет, конечно, субъективным, но должно выть основано на предыдущем опыте, а не на том, сколько страниц Дилберта мы надеемся прочитать, когда сидим за своимцтолом и делаем вид, что заняты. Поскольку план проектирования вне возможностей этой книги, больше мы не будем это обсуждать.
Безопасность Она основана на нашей диаграмме действий, приведенной ранее в этой главе!
Так как мы добавляли- вовую информацию, как только ее обяяру»: ивал и, это должно дать полную картину того, как мы должны будете реализовать безопасность. Мы мошем видеть, что есть три роли пользователей и несколько функциональных групп действий, которые следует рассмотреть. Каждый отчет также определяется диаграммой действий. Эта диаграмма действий будет вашим шаблоном для обеспечения безопасности позже при реализации действий. Окончательный обзор документации Кан только мы завершим работу, мы складываем все наши бумаги, делаем копии, распределяем их и назначаем встречу несколькими днями позже. Во время датой встречи наша цель состоит в том, чтобы получить подписи; согласовать то, что все, что требуется в нашей документации, соответствует пожеланиям клиента. Это — то, что я люблю называть отправной точкой. Как только пользователь подпишет копии документов системы, вы готовы начать создание системы. Вы больше не должны тратить много времени на пользователя, за исключением сообщений об успехах. Это не говорит а том, что наши планы незыблемы, поскольку мы, вероятно, найдем вещи, в которых не преуспели в достаточной мере. Хотелось бы надеяться, тем не менее, что любые изменения будут в форме дополнительных атрибутов, а не целого модуле таблиц вместе с новыми экранами. Резюме Цель, правда, недостижимая, логического проектирования состоит в том, чтобы обнаружить все, что нам нужно для реализации системы. Как только начнется работа по реализации системы, будет очень трудно изменить планы. Однако я предполагаю, что когда вы прочитаете эту книгу, то, по крайней мере, будете иметь здравый смысл, чтобы не продолжать процесс "проектирование — оценка — повторение" до тех пор, пока вас не охватит "паралич анализа", но найдете золотую середину, когда проект готов для реализации. На протяжении всей части книги, связанной с логическим проектированием, мы старались так проектировать систему, чтобы избегать изменения основного направления проектирования всякий раз, когда приходилось "делать откат". Мы собрали данные, взяли интервью у клиентов, детально изучили документацию, построили модели наших данных и, наконец, нормализовали их, так что данные стали близки к реализации, В этой главе мы очень приблизились к физической реализации в наших попытках завершить эту часть процесса. Рассмотрено планирование наших потребностей в формировании данных от проблем безопасности до формирования отчетов. Также добавлен подход к плану проектирования и сделаны некоторые оценки относительно размера и роста данных, которые будут храниться в таблицах. Как только эта стадия завершена, мы заново рассматриваем проект — возвращаемся, чтобы удостовериться, что мы разработали то, что намереваемся создать, и что любые изменения по сравнению с первоначальными требованиями были согласованы. Если нам повезет, наша документация будет теперь в таком состоянии, что не нужно фактически возвращаться назад и начинать все заново.
Результатом стадии логического проектирования являются чертежи наподобие тех, которые разрабатывает архитектор здания совместно е клиентом, его надежды и мечты относительно системы, далее увязка их с действительностью к физикой, требуемой, чтобы фактически построить это. Как только клиент одобрит окончательиые чертежи, вызываются субподрядчики, чтобы добавить стены, осветительную арматуру, водопровод, провода и всякие другие части, которые входят в состав физической структуры. Наши чертежи мало чем отличаются от таких чертежей зданий — с описаниями, моделями, диаграммами ат. д. для физической реализации. Следующая стадия проекта теперь уведет нас довольно далеко от этого очень абстрактного мира, в кот ором мы находились, в стадию, где мы начинаем ковать гвозди, устанавливать стены и двигаться к ВД, где наши данные могут жить и быть безопасными.
Часть II — Физическое проектирование и реализация Физическое щюектироваяме ВД й реализация — совсем afffrax ситуация по сравнению с логическим проектированием ВД, В то время как иа логической стадии мы рассматривали наш проект иеклйчительно с точки зрения получения "надлежащих” структур, в течение физической стадий проектирования мы становимся вялее прагматичными/Таким образом, мы преобразуем наш Чисто логический проект в практический проект, который уже можем реализовать. В то время как логическое моделирование — явно тяжелая часть процесса проектирования БД, физическое моделирование также не должно рассматриваться как простая задача. Если вы ранее не разрабатывали ВД, физическая реализация ВД очень отличается от реализации программного обеспечении любого другого вида, с которым вы могли иметь дело, по одной основной причине: Структуры данных в БД обычно являются центральной частью разрабатываемой системы, следовательно, большая часть кода в проекте будет связана е нммм. Поэтому любые изменения, которые мы будем производить, должны выть тщательно рассмотрены, так как они будут связаны с использованием как самих структур данных, как и кеда, который мы должны реализовать.
Помня это, мы рассмотрим следующие главы: □ Глава 9 Планирование физической структуры — В этой главе мы рассмотрим принятие решений относительно того, как мы реализуем структуру. Здесь мы начнем учитывать число пользователей, размер системы, н то, как будут использоваться данные. О Глава 10 Планирование и реализация основной физической структуры — В этой главе мы рассмотрим механику выбора типов данных, построения таблиц и создания индексов. О Глава 11 Обеспечение целостности даниих — Имеется много различных проблем, которые управляют целостностью данных нашей системы. Мы рассмотрим некоторые различные типы бизнес-правил, которые хотим реализовать, и то, как можно сделать это с учетом функций и ограничений, определенных пользователем. □ Глава 12 Расширенный доступ к данным м методы корректировки — В этой главе мы рассмотрим некоторые методы доступа к данным в созданной нами ВД, в особенности с помощью представлений и хранимых процедур. □ Глава 13 Определение требований к аппаратным средствам — Одно и» наиболее трудных действий для многих архитекторов данных — перевести требования системы в .фактические требования к аппаратным средствам ЭВМ. В каком формате диска я нуждаюсь? Сколько мне нужно дискового пространства, и как эти требования изменятся в будущем? Каков должен быть размер ОЗУ? Сколько требуется процессоров? Сжпнком много ощюсев.-ЭихЛиадо вопросы для администратора ВД, но для многих архитекторов может и не быта администратора, который поможет принять надлежащие решения относительно аппаратных средств ЭВМ. □ Глава 14 Завершение проектирования — В этой главе будет рассмотрено завершение работы. Как только мы имеем наши данные, наши структуры и- построенные запросы, проект закончен, не так ли? Чтобы завершить наш проект, мы разберем точную настройку нашей системы, включая рассмотрение проблем функционирования, связанных с формированием отчетов, особенно в интенсивно используемых системах, испытание системы и развертывание, которое мы должны предпринять. МЫ также рассмотрим составленне аварийных планов, чтобы защитить аасти данные от всех тех неудач, которые предсказал Мерфи (в его известном законе).
Планирование физической структуры Введение В этой главе мы рассмотрим, как нужно реализовать нвиуВД. Чтобы проиллюстрировать тип суждений, которые нам, вероятно, придется делать, рассмотрим следующие сценарии и попробуем оценить то, чего каждая требует для увивший реализации, типа отдельных структур только для чтения, используемых для формирования отчетов, отдельного оперативного хранилища данных или просто дополнительных аппаратных -средств, чтобы улучшить характеристики запросов. Каждый из сценариев является реалистическим примером ВД, которые являются действительно весьма банальными сегодня. В каждом случае предположим, что имеется соответствующий логический проект. Сценарий 1 ВД содержит 100 таблиц. Одна из таблиц Судет иметь миллион строк и будет расти ео скоростью одной сотни тысяч строк в день. Имеется 2000 пользователей, вводящих данные в систему, и только три человека, формирующих отчеты в течение всего дня. Сценарий 2 ВД содержит 20 таблиц. Все таблицы связаны по крайней мере с одной другой таблицей, а десять из таблиц растут со скоростью 100 строк в день. Трое людей вводят все строки двадцать четыре чма день, н то время как отчеты формируются автоматически и посылаются черве электронную почту старшим администратераи в организации. Сценарий 3 ВД имеет дна миллиона пользователей, пятнадцать тысяч из которых будут обращаться к ВД одновременно. Каждую минуту каждый из иользозателей создаст две иовых записи в ВД.
Решение Так который из этих сценариев требует специальной обработки? К сожалению, ответ неясен, так как ни один иа сценариев не содержат достаточную информацию, чтобы позволить нам принять квалифицированное решение. В сценарии 1, например, чтобы определить, нужно ли строить специальную ВД, чтобы поддержать ежедневные потребности формирования отчетов, мы выли вы должны знать, сколько истребуется времени для двух тысяч пользователей, чтобы ввести сотню тысяч строк. Это могло аавять весь день, или это могли выть строки от пользователей, которые фактически являются машинами, которые автоматически вводят данные в течение часа каждый день. Кроме того, хотя мы и можем ожидать, что данные в атом случае должны быть преобразованы, просуммированы и запасены в хранилище данных для формирования отчетов, точный характер формирования отчетов в атом случае не определен. Сценарий 2 может показаться более простым, так как мы могли бы предположить, что добавление одной тысячи строк к ВД в день — не так уж и много, особенно «ли их вводит один пользователь. Поскольку автоматический процесс управляет отчетами, мы знаем, что их формирование может выть намечено на подходящее время. В этом случае мы, вероятно, не будем нуждаться ни в какой специальной ВД для формирования отчетов, так как система не должна быть занята, а запросы - можно непосредственно иоддвятына OLTP-ВД Однако нужно учесть, что мы собираемся добавлять несколько сотен тысяч строк вгод к ВД, что, хотя конечно, и не очень большое количество строк, но может .создать проблемы в зависимости от типа данных в m.-жач и типа требуемых отчетов. Простыв агрегатные операции, которые внутренне поддерживаются в SQL Server, не будут, вероятно, вызывать никаких проблем, в то время как более сложные статистические операции могут потребовать специальной обработки. Мы рассмотрим глубже тему комплексных требований к отчетам позже в этой главе. Сценарий 3 доказателен для Web-сайта» Также сомнительно, что ов будет самым простым из сценариев для решения. Основная причина этого — то, что, если вы имеете ВД с двумя миллионами пользователей, с пятнадцатью тысячами активных соединений, вы, вероятнее всего, будете иметь деньги, чтобы использовать в этой ситуации столько аппаратных средств, сколько необходимо. Однако кроме различных проблем инвестиций в аппаратные средства, которые у вас могут возникнуть, мужв о решить другие важные проблемы относительно характера формируемых записей и требований к формированию отчетов. Эту. главу было труднее всего писать по сравнению со всеми другими главами, так как . хороший логический проект диктуется проанализированной проблемой, без компромиссов, возникаемых во время реализации, дри учете требований эксплуатационных характеристик и удобства. Хороший разработчик логической модели придет обычно к тем же заключениям для тех же самых проблем. Мы следуем одному и тому же тину методологии проектирования независимо от того, работаем ли мы с маленькой ВД, чтобы хранить телевизионное расписание, или большой ВД, чтобы хранить технические данные управления качеством. Выполнив такое проектирование хорошо гивяж^г, вы должны быть способны делать это много раз, е постоянным ожидаемым результатом. Определение, как физическое решение ВД должно в конце концов быть подготовлено и реализовано, — вообще-то другая история. Здесь мы должны взять набор параметров и построить решение, которое представляет логическую модель в виде, который позволит фирме оплатить все ее ожидаемые требоваля». Вудаяв уверены, что нелегко быть правым каждый раз. В процессе реализации вы не только рассматриваете вашу модель логического проекте, но также основываетесь на работе ваших колле- по поддержке (администратор БД и программисты БД) для лучшего выбора аппаратных средств ЭВМ, кодирования SQL-процедур и формирования преобразований из OLTP-БД в ODS, Не всегда возможно сделать оптимальный выбор, и так как сервер БД- обычно является центральной частью любой системы, он может часто вк лючать львиную долю ответственности за проблемы системы.
Данная задача может показаться вам несколько упрощенной, но не волнуйтесь, поеиольку мы теперь начнем рассматривать все проблемы предреалииадии, е которыми нам нужно разовраться перед созданием таблиц. В то время как мы не можем опивать любую ситуацию, с которой вы можете столкнуться при физической реализации, мы можем по крайней мере рассмотреть два главных фактора, на которые в состоянии влиять, я иметмог □ Формирование отчетов — Ключевой проблемой всей нашей работы не проектированию является валаис потребности в частом обновлении нашей ВД с чтением данных, чтобы обеспечить точное формирование отчётов. Поддержание средств обслуживания бизнеса очень важно для процесса формирования аргументированных решений относительно точных настроек, которые требуются для оптимизации -зффаятаиости- вианеса. □ Эксплуатационные характеристики — Эксплуатационные характеристики — совсем другой вид проблемы. Имеется большое количество ситуаций, где хорошо нормализованная ВД может обеспечить менее чем адекватные характеристики относительно того, что требуется. Мы -посмотрим ж* некоторые иа основных вещей, которые можем сделать,. чтобы регулировать характеристики, все еще сохраняя наши нормализованные структуры БД (в большиисгае случаев). Это — наше утверждение-, что первичное намерение создания систем хл иект-сервер состоит я том, чтобы позволить изменениям быть сделанными е нашими текущими данными и иметь возможность та использовать немедленно, чтобы ве отсылать запрос и ждать его завершения. Следовательно, мы должны делать то, что может сохранить ДИР-систему необремененной запросами только для чтения. Рассматривая эксплуатационные характеристики, мы не должны ставить вод угрозу целостность данных, которые мы храним. Это — основная обязанность архитектора данных хранить необходимые данные без изъянов, защищая их от аномалий, которые происходят в плохо структурированных БД. Проблемы формирования отчетов Формирование отчетов предназначено для преобразования сырого материала в значащий формат, который позволит пользователям принимать своевременные и адекватные решения. Как рассмотрено в предыдущей главе, отчеты — обычно наиболее важная часть системы для конечных пользователей, и в этом разделе мы обсудим некоторые из путев, которыми отчеты могут быть оптимизированы, чтобы вписаться в диапазон структур ВД. Возникает довольно немного проблем, когда мы обсуждаем точное и свое временное формирование отчетов, не все нз них столь же очевидны, как можно выло вы подумать, Размер данных Одна из наиболее частых проблем в формировании отчетов — то, что объем данных, необходимых, чтобы ответить яа вопросы пользователя, является слишком большим, чтобы исполнить запросы в разумной меноре. Характер проблемы зависит от аппаратных средств, на которых вы запускаете SQL Server — является ли это настольным компьютером с шестнадцатью мегабайтами ОЗУ или кластером (соединением) с четырьмя узлами по 4 гигавайта каждый. Обычно, однако, техника запросов должна быть подогнана под объемы используемых даиЛпц хотя определение, когда "маленькие" объемы станут "большими", может оказаться несколько проблематичным, как иллюстрируют следующие 1фшввры: -I - - Предположим, что Правительство Соединенных Штатов где-то имеет ВД, тсоторм сод ер ж ит все номера социального страхования, которые оио присвоило, наряду е каждой транзакщи-й,
которую -оно произвело, чтобы добавить деньги к счету или распределить деньга из счета социального страхования. Явно, что яа прошедшие; ВО с лишним лет выли вы миллионы и миллионы записей, добавляемые каждый месяц. В то время как это иожет выглядеть как большое количество данных, оно- может оказаться небольшим по сравнению с ВД, создаваемыми некоторыми провайдерами Интервета и сайтами Интернета при жвяании регистрировать каждый щелчок, каждую закупку и каждый поиск, который выполняют их пользователи. Таням образом, Web-сайты электронной торговли отслеживают стоиьяо информации о своих посетителях, -сколько могут, чтобы персонифицировать показ товаров, которые, по их мнению, будут интересны для посетителя. Такие приложения, вероятно, будут регистрировать огромнее количество информация относительно миллионов ежедневных посетителей таких сайтов, все из которых будут нужны для анализа владельцам сайтов. Другой пример регистрации операций, который становится банальным, -— производственные ВД обеспечения качества, где роботы Фиксируют сотни и тысячи измерений в минуту, чтобы гарантировать, что произведенные изделия попадают в допустимые границы параметров. Этот вид приложения, очевидно, сформирует огромное число записей. В этих ситуациях имеется невколько основных подходов, которые мы можем использовать, чтобы попробовать решить проблему: О Оптимизация — Если ша сталкиваемся е ситуацией, где нам кажется, что существует верхний предел для количества данных, с которыми наша система может работать, во многих случаях простое изменение настройки нашей системы (типа разделения данных по различным группам файлов) исправит проблему, В то время как особенности тиа большого количества индексов (пли, конечно, недостатка их) или плохо рассмотренных возможностей оптимизатора (без учета, как оптимизатор обращается с запросами), могут загубить характеристики, во многих случаях при возгласах "слишком много" можно сделать больше, даже при неоптимальных настройках сервера или БД, □ Доступ к данным — Рассмотрим случай, когда мы используем программное средство типа Асееве со связанными таблицами. Выполнение операции соединения в Access может успешно использовать добавление каждой из связанных таблиц клиенту веред созданием соединеивя, используя собственный механизм запросов, что не сложно, когда мы имеем депо е тысячами строк, и невыполнимо в случае миллионов строк. Однако запрос мог бы выполняться превосходно дри использовании SQL Server. Во многих случаях щюбяены с большими количествами данных связаны в большей мере с фактической передачей данных ио- проводу, нежели е непосредственной работой SQL. Больше о времени доступа к данным будет сказано позже в этой главе. □ Мощные аппаратные средства — Возможно, мы могли бы просто собрать более эффепявые аппаратные средства, которые работают с таким количкгйй! данных, которое нам нужно. Использование водае быстрого центрального процесюпра — ЦП (CPU-Central Processing Unit), объема ОЗУ, допустимого для данной машины, и полностью оптимизированных массивов давка не могут сдай ранить проблему, ио устранят, конечно, большую часть ее. Мы овсудим аппаратные средства несколько глубже в главе 13, □ Формирование отчетов в БД — Как мы обсудили в главе 1 и осуществим в главе 14, мы можем создать копию наших данных, которая содержит ответы на многие запросы, которые пользователь лахочет сформировать, вдавседневной работе, В нашем примере электронной торговли, приведенном выше, относительно быстрый процесс — создать кэшированную копию наших данных, чтобы отвечать на запросы пользователя. Альтернативой этому является просмотр всех записей, которые регистрировали бы все действия не только одного, но и всех пользователей сайта — действительно головная Воль реализации!
По всей вероятности» мы будем завершать работу, используя смесь этих идей, хотя первые дне — хорошая отправная точка, так как они не требуют огромных расходов капитала и являются довольно сложной задачей (но не допускайте, чтобы ваш менеджер слышал, что Вы говорите STOjl Сложность Сложность требуемого формирования отчетов непосредственно зависит от того, как мы используем основные SQL-операции, Простые запросу, исдольауя^. только встроенные SQL-операции, обычно не вызываю* никаких проблей (хотя плохо разработаиные Запросы могут вызывать неприятные ситуации, подавно пропущенному оператору WHERE и соответствующим образам запирающему всю таблицу, запрещая другта изменять ее), но не всегда возможно ограничить, вычисления только такими операциями. Например, я сформировал отчеты, которые используют единственный SQL-оператор, содержащий более чем 300 строк кода. Такая длим.кода получилась, потому что требуется около трех строк кода, чтобы выполнить стандартную функцию вычисления дисперсии, и выло необходимо 20 или около того раз ее использования внутри SGL-оп ератора, Хотя время, требуемое для выполнения оператора, не было чрезмерно большим (порядка секунд), операция оказалась дорогостоящей в терминах иетолмевания ресурсов (использование диска и ЦП}, и таким образом ограничивала параллельные операции. Если пользователи хотят видеть последние 100 вычислений этого запроса иа графике, то для выполнения вычислений каждых предыдущих 100 значений используется агрегатирование в таблице. Было бы неблагоразумным для пользователей выполнять 500 раз в день по 100 запросов в S00 строк. В этом и в других подобных случаях, которые включают чрезвычайно сложные условия формирования отчего®, может оказаться необходимым хранить некоторые предварительно агрегатированные данные, которые нарушают ЗНФ, особенно, когда доподнитеи&ные данные или бизнес-иравила требуют ответов и« одни и те же запросы. Это — иллюстрация процесса денормализацми, которого мы кратко коснулись в главе 7. Обратите внимание, что, конечно, предварительно агрегатированные данные могли бы получаться и при вычислениях вне SQL Server. При использовании предварительно агрегатированных данных вы должны гарантировать, что если основные данные изменяются, должен быть снова выполнен код, формирующий агрегатированные данные, для изменения запасенных значений. В нашем примере ише изменение единственного значения могло привести к 100 эыполнениям запроса, чтобы вое ста навить денсфмализовйнные данные. Не так уж н много можно сделать в этой ситуации, поэтому аномалии корректировки этого вида являются стержнеамм аргументом нормализации. Чтобы гарантировать целостность данных, все ссылки на те же самые данные должны Выть изменены одновременно. Требования поиска Если отчет требует большой свободы поиска, (например, когда польаователи должны выполнить запросы на многих различных полях иа многих различных таблиц), это может увеличить сложность до полной неуправляемости. Таблицы в нормализованной БД не - оптимизированы для запросов, потому что процесс нормализации пытается создать таблицы по отдельным темам. Один из способов работать с етям типом проблем — использозаш'е одной из новых возможностей, имеющейся в SQL Server 2000 — индексированные представления (Indexed Viewe). Это обеспечивает средства предварительного вычисления представления, основанного на его собственном определении, и таким образом-может использоваться, чтобы построить представление,
которое обеспечивает пользователям данные в понятном; интерфейсе, которые они далее могут запрашивать обычным епоеовом. Этот метод страдает теми же самыми проблемами характеристик, когда размещаются наши собственные денормалнзованные данные, так как SQL Server должен обслуживать индексированное представление одинаковым способом всякий раз, когда исходные данные изменяются. Дальнейшие детали индексированных представлений могут выть найдены в "Profeseiena/ SQL Server 2000 Programming" (Wrox Press, ISBN 1861004486). Конкуренция доступа пользователей Для любой конкретной системы, чем больше параллельных пользователей, тем больше случаев конкуренции, которые мы, вероятие, будем иметь между ними. В OLTP-операциях транзакции и запросы должны быть разработаны так, чтобы длиться как можно меньше времени. Отчеты — другой вопрос. Многие запросы, формирующие отчеты, могут иметь длительное время выполнения; совета» неудивительно иметь отчеты, которые требуют для выполнения пятнадцать мвиут, час, или даже несколько часов. Проблемы могут возникнуть, например, тогда, когда вы имеете очень терпеливого и относительно хорошо осведомленного пользователя пятой системн, кто пишет нерегламштированные запросы. Если пользователь не имеет никакого понимания, как- точно работает SQL Server, то он может формировать и выполнять запросы, которые включают многочисленные соединения больших.-таблиц данных, таким образом затрачивая много часов, чтобы завершить действия этих запросов. Таким образом он мажет в конятаом счете начать блокировать действия других пользователей, несмотря на блокирующие возможности на уровне строк, имеющихся в SQL Server 7.0, чтобы существенно уменьшить вероятность именно этого типа сценария блокировки. Однако нута, которым реализуется блокировка на уровне строк, позволяет оптимизатору выбирать между-блш^ювками на уровне строк, уровне страниц или уровне тавлмад, Когда пользователь пишет особенно плохо построенный запрос, который должен обращаться ко многим страницам в таплипе, оптимизатор будет часто наращивать блокировки иа страницы или даже на тавяицы. Часто, когда пользователи исполняют такие запросы, возможно, блокировки будут включать страницы и таблицы, которые в это время иамеаяются активными пользователями, . причиняя много сбоев! - - ’ - : - Своевременность означает, какой должен вьиь порядок потокаданных. чтобы поддержать оотребиости пользователя в принятии квал ифицированных решений. Например. предположим, что мы формируем оперативное хранилище данных из наших OLTP-данных, заполняем хранилище данных и витрииы данных- иа оперативного хранидища данных -а освежаем данные ежедневно. Вся эта система будет бесполезна пользователю, если все решения должны быть сделаны, используя только самые посдедаие данные. Как быстро мы можем обеспечить доступ к данным, зависит яе только от количества времени, которое требуется для передачи данных с одного места -другое (мя называемое время ожидания), но также и от нашего проекта. Наши различные подходы могут быть подытожены следующим образом: □ Немедленный — Немедленный доступ наиболее проблематичен, так как мы можем сделать очень мало для его обеспечения. Имеется много способов обеспечить эти потребности, и все они вызывают проблемы функционирования. Мы должны будем часто строить лишь хранимые процедуры, которые читают данные непосредственно от наших таблиц, ©вращаясь к данным таким образом,.-чтобы избежать их
блокировку. Если это невозможно сделать без организации "попадания”. то мы можем включать деиормализованиые структуры или, возможно, даже индексированные представления в наших хранимых процедурах. Иногда орган изация пс-иадакия неизбежна и фактически представляет допустимый недостаток в наших потребностях представления денных. В равной степени мы можем придумать компромисс, типа вероятностной выборки деяиых вместо включения каждого значения, что улучшит нашу скорость поиска данных. □ Некоторое допустимое время ожидания — Если вам не нужно мгновение читать данные, мы можем использовать любую из других методологий. Можно создать структуры в оперативном хранилище данных, которые обновляются настолько часто, насколько позволяет размер данных (и бизнес-правила). Нужно позаботиться о том, чтобы гарантировать, что частота обновления выла больше частоты обращения пользователей. □ Большое время ожидания — Когда время — не основная проблема, и особенно когда вовлечено чрезвычайно большое количество исторических данных (типа анализа тенденций через какое-то время), хранилище данных или сопутствующие витрины являются лучшим средством, потому что мы можем иметь больше возможностей (я все они часто используются) агрегатирования и настройки запросов. Частота Частота в атом контексте означает число раз получения отчета, Одна из систем, над которой я раньше работал, содержала отчет, который считался самым яижным в ее функционировании. Он запускался каждые 24 часа в полночь и занимал для своего формирования три часа. Так -как он за пускался в наименее важные «асы суток, на работу пользователей его мало влияло. С другой стороны, у многих других систем пользователям нужно иметь актуальные таблицы в течение дня, например, менеджер банка, который должен быть уверен* что банк имеет достаточно денег на руках, или брокер, кто должен иметь самые последние отчеты с тенденциями но каждому из клиентов или фондов. Формирование отчетов этого типа будет, вероятно, требовать супермощных аппаратных средств й чрезвычайно оптимизированных запросов, а также некоторые денормализованные структуры. Влияние характеристик Если мы возьмем пример ВД, созданной для контроля и хранения результатов деятельности потребителей на Web-сайте, которую мы обсуждали ранее, мало того, что эта ВД будет логически спроектирована, чтобы регистрировать каждую операцию, мы также могли вы размещать каждый элемент н каждую странивд, которые отображены на экране. Это очень большая БД (думаю размером в терабайты), и с ев данными, размешенными нормализованным образом, единственная ВД, вероятно, не была бы способна выдержать обе группы пользователей — пользователей, изменяющих данные,, и пользователей Web-приложения, запрашивающих его страницы. В следующем разделе мы рассмотри?,: некоторые возможные компьютеры я конфигурации сетов (обычно называемые топологией), которые будем использовать, чтобы бороться с множеством общих проблем - функционирования. » -.1868 239
Скорость соединения Первостепенный интерес здесь имеют глобальные сети (Wide Area Networks — WAN) наряду e Web-приложениями (которые можно фактически расценивать как глобальные сети, но в другом облачении). Возьмем очень простой пример— классическую двухъярусную систему клиент-сервер: Изменение скорости соединения или расстояния между клиентом и сервером ВД существенно трансформирует окончательное решение но организации нашей СУБД. В следующем перечне мы рассмотрим некоторые общие скорости в сети, и как оии повлияют иа наше решение изменить эту простую топологию: □ Чрезвычайно быстрое соединение типа 10 Мбит, 100 Мбит или больше — эти типы соединений наиболее характерны в пределах физической сета компании. Если мы не имеем проблем с размером БД, то нетрудно использовать простую двухъярус ную систему клиент-сервер, как показано на рисунке выше. Пользователь может присоединяться непосредственно к SOL Server, и требуется очень небольшая специальная оптимизация. □ Умеренное соединение типа ISDN (Integrated Services Digital Network — цифровая сеть связи е комплексными услугами), DSL (Digital Subscriber Line — цифровая абонентская лиижя), кабельного (от 2 Мбит до 128 Кбмт) соединения — обычно освоввые приложевия клиент-сервер реализуются о такими соединениями, но нам нужно быть уверенными, что мы оптимизировали все наши запросы к БД, чтобы уменьшить число байтов, которые передаются туда и обратно по сети, и число различных используемых коммуникаций» Возможное улучшение могло вы выть получено, оптимизируя наше программное обеспечение клиента, кэшируя частые запросы к БД, которые могут повторяться, и группируя многократные действия вместе. □ Медленное соединение типа коммутируемого соединени я (Dual-up) (в принципе 56 Квит, яо более часто примерно 28.8 Кбит) — приложения с коммутируемым соединением более распространены, чем мы могли бы себе представить. Многие служащие, связанные е продажами или обеспечением, используют коммутируемые соединения для управления приложениями ВД по ночам из своих гостиниц, Хотя пользователи, осуществляющие доступ к своим приложениям через коммутируемую связь, вообще-то чаще имеют в виду скорость доступа, при которой приложения должны работать соответственно. В этих случаях мы, конечно. должны будем оптимизировать приложения или, что даже более возможно, применить схему тиражирования части ВД клиенту, используя некоторую форму сжатия. Мы рассмотрим тиражирование позже в этой главе.
Независимо от быстродействия рабочей станции или сервера ВД окоргх’ть, е которой вы соединяетесь с сервером, является критической с точки арения производительности. Веди вы пытаетесь передать 1000 строк назад клиенту, где строка содержит 3000 байт, и вы осуществляете ото по 128-килобайтоиой ISDN-линии, вашему клиент;,’ будог, конечно, плохо с вами, так как будет требоваться приблизительно 3 минуты ((3000 * 1000 * 8 (вит на байт)) / (128 ♦ 1000) «• 187.5 секунд), чтобы закончить передачу. Это не учитывает издержки ни в самой сети, ни за счет любых других пользователей. Вели бы десять лет назад пользователи попытались использовать ту же -самую полосу пропускания, то потребовалась бы дополнительно 31 минута для получения своих данных! Следует отметить, что вы могли бы написать вашу программу так, чтобы восстанавливать записи и отображать их по мере необходимости вместо восстановления всех 1000 записей в одно и то же время. Например, вы могли бы показывать по 20 записей на одной странице, и посколысу пользователи- ' .. просматривают результаты, остающиеся записи были вы тогда разделены на части. Однако для всех из нас, кто использовал анализатор запроса и возвращал дополнительно 1ООО строк для тестирования запроса в помвкунды (включая и запрос, и выборку данных), ото все еще была бы неприятным. Действительность такова, что очень немногие большие системы сегодня строятся с простой архитектурой клиент-сервер. Волыпинство из них имеет распределенные части по всей сети, и даже по всему миру. Следующий пример — проект, который выглядит довольно дико, и который включает Web-серверы, хранилища данных и даже, как наследство, систему универсальных ЭВМ.
Из этого рисунка вы можете видеть, что сложность нашей» решения ограничена только фантазиек архитектора. Некоторые организации имеют десятки или даже сотни серверов ВД, многие из которых связанвместе о помощью различных вадов соединений, обладающих различными показателями надежности. В этом случае каждая линия между элементами представляет стязь сети. В джАаж из этих мест связи мы имеем скорости, которые могут изменяться от чрезвычайно большой до чрезвычайно медленной, и каждое место связи должна рассматриваться как ответственное (с точки зрения проекта), так как сети имеют свойство быть меньше чем 100-процентной надежности. В то время как для проектировщика БД важно, чтобы, по крайне! мере, знать, как скорость соединения влияет на работу приложений, более широкие аспекты проблем организации сети попадают в сферу деятельности системного администратора. Количество данных SQL Server 2000 — широко масштабируемый сервер БД. Он масштабируется w карманного компьютера с ОС Window черва отдельный ноутбук Pentium-200 МГц с тридцатью двумя мегабайтами ОЗУ и Windows 95 или двухпроцессорный Penttum Ш с половиной гигабайт ОЗУ до кластера из шестнадцати серверов, каждый из которых содержит по восемь процессоров с четырьмя гигавайтами ОЗУ каждый, и которые связаны друг с другом. Количество данных, которые вы можете поддерживать, изменяется от мегабайта или двух у вашего карманного компьютера до многих терабайт у группы серверов. Размер данных будет существенно влиять на изменение наших стандартов кодирования. Мы можем легко отойти от некоторых удобств программирования (игнорирование оптимизации и подобные), если наша основная таблица в ВД будет иметь одну тысячу записей в противоположность одному миллиарду. В качестве иллюстрации возьмем следующий простой пример: i child I childld сЫЙ attribute 1 child attribute 2 child attribute N parentld (FK) parent_____________ parentld parent attribute 1 parent attribute 2 parent attribute N Здесь child — таблица "потомок", childld — идентификатор потомка, child attribute — атрибут потомка, parentld — идентификатор предка, parent — таблица "предок", parent attribute — атрибут предка. Мы заявим как определение, что, если вы имеете запись в таблице-потомке, должен существовать мигрирующий ключ из таблицы-предка. Теперь рассмотрим ситуацию, когда мы хотим удалить запись из таблицы-предка. Тогда становится очевидно, что размер таблицы-потомка будет чрезвычайно важен, Вели в таблице-потомке имеется десять строк, то чтобы удалить одну строку в таблице-предке, вы должны просмотреть девять записей — не так уж много. Однако если имеется миллион строк, это — не столь уж простая задача для SQL Server (даже е индексами это могло потребовать непомерное количество времени). Из этого следует, что, если вы имеете миллиард строк в таблице, строку не удастся удалить даже в две недели. В этом случае мы, вероятно, поместили бы в таблицу-предок
дополнительный столбец, чтобы указать, что запись вольте ве может использоваться, но не фактически удаля®-запись, Однако задание этого флага в тавлице-предае оставит записи и в таблице-потомке, связанные с помеченной флагом записью, и эти записи в. таблице-потомке требуют осторожной обработки, Следовательно, в любом случае мы не можем управляет ситуацией оптимально. Бюджет Любые решения, которые мы изобретаем, должны выть осиоваиы не. том, что подходит для организации, так как увеличение размера бюджета, чтобы оплатить дорогие аинаратные средства, не всегда может быть приемлемым. Имеются три главных области, связанные с этим: □ Аппаратные средства — Недостаточно сказать, что нехватка аппаратных средств вызовет у вас проблемы. В главе 13 мы рассмотрим определение количества аппаратных средств при увеличении БД, в зависимости от числа пользователей и т, д. Простой принцип — никогда не урезайте производательноеть аппаратных средств, В главе 14 ми обсудим довольно глубока концепцию наличия по крайней мере трех различных видов оборудования — для разработки, испытания и производства. Чтобы создавать, проверять и реализовывать надлежащую СУВД, вам будет нужно, по крайней мере, два отдельных сервера. □ Программное обеспечение — Бюджет должен быть достаточен для ОС, лицензионного программного обеспечения БД и любых программных средств, которые вам потревуютея для создания программного обеспечения. Внимательно читайте лиданзиоаные соглашения по программному обеспечению, чтобы избежать переплаты за вето. □ Укомплектование персоналом — В зависимости от размера проекта, вы должны быть готовы иметь штат, способный работать с каждой частью СУВД, которую вы разрабатываете. Очень немного архитекторов ВД являются также- и большими специалистами но организации сети, большими администраторами ВД, большими документатора?™, большими испытателягчи, большими программистами и большими ... -— поставьте точку сами. Число пользователей Одной из наиболее трудных характеристик, е которой приходится иметь дело, является число пользователей, которых вы должны будете поддерживать. Поскольку каждое соединение требует приблизительно 12 Кб + (3 * (размер сетевого пакета, который изменяется, яо по умолчанию равен 4 Кб)), или 24 Кб, большинство систем»» архитекторов -те очень обеспокоено числом пользователей, которых система будет имяьг Одаяко нынче, когда ВД поддерживает Web-сайты, общее количество пользователей, о^явдюиихея® нашей ВД, может быть где-нибудь от -10 до 10 миллионов или даже больше' В попытке иметь дело с таким большим числом пользователей используется ряд иетодЭД включая: □ Организация пула (накопителя) соединений — Если пользователь в своем приложении соединяется и разъединяется с данными много раз, может быть выгодно использовать евмоижости организации пула в ODBC В.б (и последующих версий) или OLE DB, Они позволяют организовать драйверы так. что пользователь "думает", что он отключился, но фактически сохраняет связь открытой, ожидающей дальнейших команд.
Как краткое предоетережекие, арганиващи- пула соединений- НЕ производит, собственную очистку, когда вы сообщаете, что соединение завершено. Наиболее- важно не закрывать любые открытые транзакции, которые могут затрагивать более поздние соединения, даже в случае многих пользователей, использующих соединение, поэтому должно быть выдана соответствующее предостережен ие. □ СОМ+ (или любая разновидность а-яруеиого приложения) — СОМ+ позволяет вам иеренести часть обработки, которая могла бы выть выполнена яа стороне клиента, и организовать пул соединений среди многих различных пользователей, так что вы устраните добрую часть непроизводительных издержек, связанных с созданием и разрывом соеданений, При организации пула соединений десять тысяч параллельных связей может напоминать 1000 или даже 100, □ Увеличение производительности, увеличение памяти — Только добавляя большее количество аппаратных средств, черт побери! К сожалению, ограничения бюджета могут означать, что это решение невозможно. Как ранее упомянуто, каждое соединение требует - 24 Кб ОЗУ. Это может казаться не слишком большой проблемой, но если вы имеете 10000 соединений с сервером, величина могла бы равняться 240000 Кб, что является существенной частью от 256 Мб, которые может использовать стандартный SQL Server. Вели ми снова и снова добавляем ’к накладным расходам процессора управление всем этим ОЗУ, мы быстро увидим, как возникнут проблемы. О Кластеризация серверов — Microsoft SQL Server и Windows 2000 поддерживают кластеризацию нескольких серверов ВД и работу с ними как с единственным сервером. Использование кластеризации позволяет нам создавать очень большие, очень мощные серверные системы ВД, Идея таких систем состоит в том, чтобы распределить груз приложения на ряд серверов, что приводит к увеличению выполняемых запросов, гак как могут быть добавлены дополнительные серверы для обслуживания дополнительной нагрузки. Этот процесс известен какпалансярлпапир нагрузки, 'Гак как каждый сервер (в идеале) может обрабатывать требования клиента индивидуально, то если один сервер сломается.или потребуются дополнительные серверы, добавленные к кластеру, в работе не будет никакой.неприятности. Дальнейшие детали относительно кластериаации могут быть найдены в "Professional Windows ША: Building Distributed Web Applications with VB, COM-. MSMQ, SOAP, and ASP” (Wrox Press, ISBN 1861004461). □ Приложение co специальными копиями данных только для чтения — Это — технология, которая хорошо работает при создании Web-сайтов. Большинство данных, которые нам нужны для формирования Web-страниц, — только для чтения. Например, рассмотрим гипотетическую ВД описания изделий, в которой мы имеем все наши изделия, размещенные в хорошо нормализованном наборе структур, избегая все ловушки избыточности. Далее рассмотрим Web-страницу, которую пользователь будет видеть, и где на одной странице перечислены текущая цена, цвет, размеры, формы, спецификации и т. д. В этом случае мы могли бы построить единственную таблицу, которая имеет все значения, необходимые для Web-страницы, вместо пятидесяти таблиц и трех сотен строк. Мы, вероятно, не хотели бы предварительно формировать «сю таблицу пользователя, так как, конечно, хотели бы иметь возможность персонифицировать ее (добавить скидки и т. д.) и. конечно, изменить его взгляд и намерения относительно предмета. Потребуется дополнительное хранилище, ио требуемая обработка существенно уменьшится, благодаря сокращению числа выполняемых соединений.
Возможности SQL Server Рассмотрев многие не проблем, касающихся функционирования наших СУВД, мы должны также рассмотреть средства, которые SQL Server предлагает проектировщику, В этом разделе мы кратко- рассмотрим, что они совой представляют н как могут использоваться прежде всего теми, кто яе знаком-близко с SQL Server. Для тех, кто является постоянным пользователем SQL Server, — пожалуйста, пропустите этот раздел вплоть до заголовка "Примеры основных топологий" в следующем разделе, Т премирование Тиражирование — замечательное средство, которое помогает в создании копий ВД и поддержании их для нас в некотором интервале. Этот интервал может изменяться от "немедленно" до дней или недель. Тиражирование может быть выполнено между различными SQL Server или даже из SQL Server в Microsoft Асееве, Oracle, или почти любую ВД, которая поддерживает требования к подписчикам для SQL Server (для дополнительной информации ем. SQL Server Books Online). Пара терминов, которые нам нужно здесь ввести: □ Публикация — источник копируемых данных, хотя это может или ие может быть первоначальным источником. Подобно физически напечатанной публикации (следовательно, имеющей название), публикация — это БД, которая была отмечена для других ВД, чтобы они могли подписаться на нее. БД разбита иа таблицы, помеченные дли тиражирования ответа, на которые ссылаются как иа статьи. □ Подписка -— когда БД использует публикацию, чтобы получить точную копию одной или нескольких статей этой публикации, то ее называют подписчиком. Подписка может быть как на одну статью, так и на все статьи, SQL Server обеспатавает три различных модели для реализация тиражирования: □ Тиражирование копии — периодически делает копию данных публикации и заменяет полный набор данных у подписчика. □ Тиражирование с помощью транзакций — первоначально делает копию публикации подписчику (точно так же, как тиражирование копии), а затем использует любую транзакцию асинхронно к подписчику, как только копия возникнет у публикации. □ Тиражирование слиянием — позволяет вам иметь две БД (одна яз которых выбрана как издатель, формирующий публикацию, а другая — как подписчик), где вы берете все изменения от издателя и добавляете их к подписчику и наоборот. Это позволяет вам редактировать данные в любом месте и использовать изменения в другой ВД автоматически. Тиражирование слиянием очень полезно для передвижных клиентов, когда поддержание связи с основным сервером невыполнимо. Оно также имеет варианты по умолчанию и специальные, чтобы разрешать конфликты в случае, когда несколько пользователей редвкифуют одни и те же части данных.
Связанные серверы Связанные серверы позволяют нам организовать доступ к неоднородному SQL Server или источникам данных OLB DB непосредственно изнутри SQL Server. Мы не только можем обеспечить доступ, но и можем «ыволиять для этих данных изменения, команды и транзакции, Например, для доступа к SQL Server по имени LOUSERVER, мы выполнил» вы следующее: В вр__add 1 inkedserver (server - H'LcnkServer’ , «provider - N'SQLOLEDB', В Sdatasrc - S'LOUSERVER', . . В ScaEaiog — N'Pubs' Затем мы можем выполнить запрос, чтобы получить строки из этого источника данных: SBLBCT * В FROM LinkServar.pubB.dbo.authors. Это — простой пример, но он кратко иллюстрирует наше утверждение. Со связанными серверами вы можете иметь доступ к дополиигольным SQL Server, серверам Oracle, ВД Access, электронным таблицам Excel лишь по их. именам, И прелесть этого состоит в том, что вы не должны изучать виквкой дополнительный синтаксис доступа к несметному числу различных типов данных. Уродливая сторона этого заключается н том, что для присоединения одного набора данных к другому набору, возможно, придется большое количество данных пропустить иа одного SQL Server во временно:» хранилищ» яа другом прежде, чем будет выполнено соединение, что может иметь серьезные папочные эффекты для работы. Сервисы преобразования данных Для случая, когда тиражирование не будет работать из-за изменений (а, следовательно, и изменения названия), еделаваых в данных, фирма Microsoft обеспечила сервисы преобразования данных (DTS — Data Transformation Services). Используя их, вы можете преобразовывать и передавать данные между источниками OLE DB. DTS также позволяет вам передавать индексы и хранимые процедуры между SQL Server. Используя DTS, вы можете создавать пакеты, которые гру/ыпфутт икаете многократные операции преобразования/объекты, которые могут быть запущены или синхронно (одна операция ожидает завершение другой) или асинхронно, используя все возможности SQL Server, Вы можете, например, выполнять поочередно очистку данных (удалеиие/чистка. данных) в таблицах, при втом имеется возможность вычислить новые значения, используя простую программу на Visual Basie. DTS — полностью специализированный инструмент очисткж данных и имеет множество настроек и возможностей. Дальнейшие детали могут Выть найдены в "Professional SQL Server 2000 DTS", (Wrox Peeta, ISBN 1861004419). Контроллер распределенных транзакций Контроллер распределенных транзакций (DTC — Distributed Transaction Controller) — средство, которое осуществляет двухфазную фиксацию транзакций, что позволяет вам создать транзакцию, которая использует больше чем один сервер. Причина, по которой
процесс называется двухфазной фиксацией, заключается в том, что на каждый из серверов, который ишюльзуетея транзакцией, посылаются две специальные команды, чтобы начать и закончить процеее. □ Подготовка — менеджер транзакций посылает команду каждому серверу ВД для его менедасера ресурсов. При атом сервер готовится принимать команды, которые будут посланы, □ Фиксация ми откат — как только "пояьж»вн*ваь(Е)ф сформировал любую задачу и выдает команду, что он хочет ее выполнить, начинается фаза фиксации или отката, работая наподобие того, как вы вшюшяли бы фиксацию транзакции на каждом сервере. Строго в манере кодаровани.я для SQL Server, если DTC был включен и вы еоадали связанные серверы, то можно использован, следующий код, чтобы модернизировать таблицу авторов как на сервере, где этот над выполняется, так и на связанном сервере. USt pubs GO BEGIN DISTRIBUTED TRANSACTION UPDATE authors SET a-u_lnama - '’Davidson* WHERE au_id - *555-55-5555' IF Rgerrcr о 0 BEGIN ROLLBACK TRANSACTION GOTO exit END UPDATE linkserver.pubs.dbo,authors SET : a:'Davidson' WHERE au_id - '555-55-5555' IF Sfjerror <> 0 BEGIN ROLLBACK TRANSACTION . - GOTO exit END COfMIT TRANSACTI0»T GO Обратите внимание, что контроллер DTC работает с данными любого тина, для которых существует драйвер OLE DB, который поддерживает интерфейсы распределенных транзакций. Следует также, знать, что если DTC не установлен, оператор BEGIN DISTRIBUTED TRANSACT IOW не будет выполнен. Объекты управления данными SQL Объекты управления данными SQL (SQL DMO — SQL Date Management Objects) ~ набор СОМ-об'ъектов, которые включают почти каждую особенность SQL Server. Из DMO вы можете выполнять задачи типа создания таблиц и индексов, создания объектов
автоматизации и задач администрирования. Объекты могут иепажмоваться из любого программного средства, которое допуска» реализацию СОМ-объектоз, типа Visual Basic, VB Script, TO far Application», C++ и, как вы увидите в следующем разделе, самого SQL Server, Есть много хороших примеров в SQL Server Books Online, которые иллюстрируют мощь SQL-DMO; дальнейшие детели можно также найти в "Professional SQL Server 7.0 Development Using SQL-DMO, SQL-NS & DTS", (Wrox Press, ISBN 1861002807 J. Реализация COM-объектов Другая изящная особенность, которая существует с версии в.Х SQL Server — реализация COM-объектов. Используя симпатичный мощный интерфейс Т-SQL, вы можете вызвать большинство COM-объектов ж делать с ними почти все, что вам потребуется. В следующем примере мы реализуем DMO-oc-ъект SQLServer и затем соедииимся. с ним: DECLARE SobjectHandle int BrecVai int, --реализация объекта eqlserver EXECUTE SretVal - sp_OACreate 'SQLDMO.SQLServer*, gobjectHandle OUT IF SretVal <> 0 BEGIN EXECUTE sp_displayoaerrorinfo 8ebJectHandle, SretVal RETURN END —связь с сервером EXECUTE gretval • sp_OAMethod gobjectHandle, 'Connect', NULL, 'LOUSERVER', 'louis', ' <не ваше вело, какой у меня пароль>' IF gretVal О О BEGIN EXECUTE sp__displayoaerrarinfo BobjectHandle, gretVal RETURN END Хотя реализация COM-объекта и очень полезна, она может при атом быть медленным процессом. Объект должен быта создан (и память для него выделена), исполнен и, наконец, удален — все не в стиле SQL. Используйте эти функциональные возможности, только когда основанное m SQL решение невозможно, или в процедурах, которые вы не раеешпываето использовать часто. Почта SQL Почта SQL (SQL Mail) позволяет системе SQL Server посылать и получать почту, используя простой интерфейс Outlook или Exchange либо серверы Exchange или POP3 (почтовый протокол Интернета), Сервер может быть сделан так, чтобы отвечать на сообщения электронной почты, которые содержат запросы, возвращая результаты ответа так же, как сообщения электронной почты. Он может быть также добавлен к хранимым процедурам и триггерам, если это потребуется. Примерами того, насколько это полезно, могут быть задачи типа посылки напоминаний в конкретные пункты по календарю или даже посылка предупреждений, чтобы сообщить пользователю, что произошло некоторое событие типа отрицательного баланса на его счете в банке, В следующем фрагменте кода мы используем почту SQL, чтобы послать очень приятное сообщение по электронной почте:
IEXEC xp_sendmail @recipients « ' уавгпамвасгалп.соп!'', flquery » 'select "hi"- *, gsubject “ 'SQL Server Report', ftneeeage » 'Hello!', eartach_results - 'TRUE’ Полнотекстовый поиск Полнотекстовый поиск с помощью SQL Server позволяет нам строить очень мощные средства, которые работают подобно любому поиску сайта в Интернете. Использование полнотекстового поиска позволяет нам искать в столбцах текста так же как, во внешних системных файлах. Синтаксис тапроса для полнотекстового поиска —просто расширение обычных команд SQL Server. Например, рассмотрим следующий запрос: I SELECT title_id, title, price FROM pubs..titles WHERE CONTAINS (title, '"Databaae" near “Design"') Используя ключевое слово CONTAINS, мы ищем не только названия, которые содержат Database и Design, но и названия, где они встречаются в сочетании близко друг к другу. Обратите внимание, что хотя полнотекстовые способности добавлены в SQL Server 2000, они не устанавливаются-по умолчанию. Вели вы попробуете выполнить этот оператор spubs без конфигурации ВД публикаций, а также конфигурации столбца названий в таблицеtr'l-ra (названия) для полнотекстовой поддержки, пример, приведенный выметив будет выполнен с сообщением об ошибке, Полнотекстовые индексы размещаются отдельно or SQL Server и поэтому могут быть медленнее, чем собственные операции SQL Server. Дополнительные детали о-тноеительно-установки и осуществления полнотекстового поиска могут быть найдены в ''Professional SQL Strver 2000 Programming” (Wrox Press. ISBN 1861004486). Агент SQL Server Последним в нашем разделе, посвящениом возможностям SQL Server, и далеко не самым последним, является возможность планирования работы с помощью агента SQL Server, который является связующим элементом, обеспечивающим работу вашей системы. Тиражирование использует агента, чтобы обрабатывать транзакции, пакеты DTS могут быть запланированы не выполнение в определенное время, и вы можете также запланировать ваши собственные хранимые процедуры, чтобы выполнять и запускать программы ОС. Установка агента SQL Server — вне возможностей этой книги, и читатель должен проконсультироваться относительно деталей в SQL Server Books Online. Важно отметить, что SQL Server имеет встроенные возможности, которые запускают свой собственный контекст безопасности, который будет выполняться почта каждый раз, когда будет требоваться ваша ВД.
Примеры основных топологий В этом разделе мы рассмотрим некоторые ваиболее важные топологии, которые могут быть выбраны при разработке наших приложений. "Тонкий” клиент в сравнении с "толстым" клиентом “Толщина” клиента непосредственно емшана с таи* за какую часть работы отвечает программа клиента. Вт® ив следует путать с тем же самым термином, используемым в описании концепции программы, написанной на классическом языке программирования вроде Visual Basic или C++, где все представление логики закодировано в программе клиента в противоположность выделенному клиенту. "Толстым'* клиент Лучшим примером “толстого" клиента Выло бы приложение, написавное на Visual Basic, которое организует доступ к БД текстовых файлов, используя ODBC (Open DataBase Connectivity — открытый интерфейс доступа к базам даиных). Так как система для текстовых файлов, конечно, не СУРВД и поэтому не может защищать себя, вы должны включить в клиент код проверки достоверности каждого бита данных. Использование очень "толстого" клиента может уменьшить влияние помех приложения, потому что, воли каждая возможная проблема с данными обрабатывается клиентом, и-нет никакой возможности серверу изменить какие-либо значения, мы можем полагать, что наши данные будут сохранены точно так, как мы ввели их а БД. Если происходит ошибка, то это будет на-аа проблемы. ж SQL Server (типа отказа диска или некоторых видов перчи данных). Однако "толстый” клиент ив вез своих недостатков, Так как вы уже иредварительно проверили данные* кажется чрезмерным иметь SQL Server для их проверки. Но с другой стороны, если только клиент — не единственный метод получения данных из сервера, вы оставляете ваши данные открытыми для ошибок всякий раз, когда с ними осуществляется работа не иа приложения. Это может казаться маленькой ценой, которую мы платим, чтобы иметь дружественное приложение, но редко все редактирование данных можно выполнить строго с помощью единственной частя программного обеспечения. "Тонкий” клиент На другом конце спектра — тонкий клиент. В атом сценарии код клиенте «всем не проверяет вводимые данные, и считается, что механизмы SQL Server будут заботиться о любых ошибках и сообщать о них. Хороший пример очень "тонкого” клиента —- окно запроса SQL Server BnterpriBe Manager:
.Г о. SQl Swvw t М#*г»н^ • р Dabs in 1 аЫ« “аЫЬон*! 9ВЯ1П1И ‘1 J 2-СП5ЕЙ ndovjJ .Не^= гтф - - !_'.‘ l; , a=j v о lai Irarre lac !WSre:- ’• liters ‘ -address : led н П2-ЗЫ1И 1Ш*в iJbhwfin 10932 :^gt Rc. 213-46-3915 Sen Msrjorfe :«59в6-ДЙ(3 :0<. Л15 548-7'23 539-)г;МпЬп, E 267-41-2394 Oleary Whtei 406 286-2428 22 6te'*jirdAl. < гМ-вМЭИ teal *15 834-291Э ISHffiCiiegaAv. =СЦ 3-И-22-1782 Фг§д^7р;Фзфотн Me-Kider .мзачз-стг 10 Mississippi Or I 409-56-700? Bennet Afcrabem .415658-9932 йИбетипа f 42М?-2Э1$ 1Лт 415836-7126 3410 Истое 5t f 472-27-2349 Grtntfssby art -7в793ЭДИ5 POBc-792 < 466-29-1766 Lodeslay Charter» 415 535-4620 J18Broadway^. JSM 527-72-3246 &eene Morningstar 615 297-2723 ' 22 &ayb» House If 646-42-1672 . &ше»А 503 745-6402 155 НИЛЛ 672-71-3249 :¥otamoto AIM 415 935-4228 ЗЯигО. 1 712-45-1967 ,<tolCaefc ‘«5996-9275 2236 Cram ₽!. #86 1 И-51-&Й4 -DBFrance =МкЬЫ 219 547-9982 ЗЕаИмЯ. < 724-03-9931 SOTger 4’5 843-2991 724-ОМ381 MacFealher ф-Ямгл$ фнфф 415 354-7128 |*г1ШйНб*. 1 * 1 756-33-7391 > JfiS 415 534-92’9 5720 M-:Are---St. i 1Л: ' Здесь au__id — идентификатор автора, au_lname — фамилия автора, au_fname — имя автора, phene — номер телефона, address — адрес. Независимо от того» что вы будете вводить а пустые места, входная форма будет брать это и передавать к SQL Server. Он, в свою очередь, будет брать данные и размещать их, пока ие будет проинструктирован, чтобы не делать это. Одни из важных моментов использования "тонких’ клиентов SQL Server для редактирования всех данных — то, что вы никогда ие должны волноваться относительно того, откуда поступают данные, так как, если вы закодировали ваш сервер должным образом, все данные ВД будут чисты. Данные к тому же проверяются однажды и только однажды, Это улучшит работу ваших приложений, особенно, когда случаи ошибок очень редки. Обратная сторона — уменьшение используемости приложения. Так как приложение понятия не имеет о том, как должны выглядеть данные, оно не иожет уберечь пользователя от ошибок. Это особенно видно в приложениях, которые имеют правила, затрагивающие более одного столбца. Возьмем следующий пример: Здесь Email Address — адрес злектронмой почты. Send email notifications — послать уведомления па электронной почта, Emai 1 те offers — электронная почта мне предлагает.
Легко видеть, что мы должны иметь правиле- предотвращения проверки любого из этих выключателей, пока пользователь яе имеет адреса электронной почты. В ВД это просто осуществить, но если мы не запрограммируем какую-либо иа подабиыж логик у клиента, нет никакой возможности- избежать следующей ситуации: Здесь выводится текст: [Microsoft [[Драйвер ODBC SQL Server][SQL Server] Оператор INSERT конфликтует с оерагшчлнием TABLE CHECK 'chkSemallAddresSemailBlankN^Ihecks*, Конфликт, возник в ВД 'Pubs’ (публикации), таблице 'аМ_етаД (электронные адреса авторов) [Microsoft][Драйвер ODBC SQL Server][SQL Server] Оператор был выброшен Мы могли бы придумать довольно сложную схему отображения ошибок, чтобы выводить ошибку в более дружественном формате, однако основной процесс никогда не будет очень хорошо воспроизводить конкретную ситуацию. То, что в промежутке Что наиболее для нас приятно, так это "где-нибудь промежутке". Идеальные приложения клиент-сернер (или n-звениая структура подобного типа) использовали бы ВД в НФДК с клиентом, который контролирует наименьшее количество данных, необходимых для обеспечения потребностей пользователе, а выли бы способны обрабатывать любые сообщения об ошибках, которые возвращаются от SQL Server. Некоторые типы правил не подходят для обеспечения на стороне клиента, к ним относятся: □ правила связей между таблицами; О уникальность данных; □ отношения через внешние ключи. Для этих правил вам, конечно, будет нужно позволить серверу обрабатывать ошибку и создавать средство для отобраскения пользователю информации об ошибке в разумной форме. Если мы вернемся к нашему предыдущему примеру в форме Visual Basic, нам бы потребовалось сделать кое-что подобно следующему:
Здесь даа выключателя выли заблокированы, чтобы пользователь не мог взаимодействовать с ними, ножа не заполнен адрес электронной почты. Как далеко мы пойдем с предварительной проверкой данных — это осмотрительная смесь практичности с удобством сопровождения (например, изменения к схеме данных могут также потребовать изменений в программе ва Visual Basic и, как следствие, повторного испытания и схемы, и кода на Visual Basic). Если вы мы хотели проверить, что напечатанный адрес электронной почты правильный, мы могли вы просто поручить системе SQL Server выполнить контроль, используя проверку ограничений. и отобразить результаты проверки а подходящем сообщении для пользователей. Клиент и конфигурация данных Теперь мы посмотрим на некоторые иа основных конфигураций, которые, можем использовать при создания систем сервера ВД. Хотя группа, которую мы рассмотрим, ие является всеобъемлющей, она, определенно, даст нам основу для выбора настроенных конфигураций, которые решают наши проблемы. Классический клиент-сервер Старый, верный, и все же все еще важный после стольких лет клиент-сервер! Десять лет назад это была очень воняя конг.еппия. Девять лет и триста шестьдесят четыре дня назад (Яя лгол'снт выхода в свет перевода этот. срок существенно изменился. Прим, перев) все мы начали придумывать лучшую конфигурацию, и хотя многие приложения теперь n-звенного типа, клиент-сервер все еще подходит во многих ситуациях, когда используется SQL Server. Обычно вы имеете одну или несколько районах станций, непосредственно соединенных с сервером БД SQL Serve’’, Соединение должно быть разумно быстрым, так как в первую очередь это нужно для передачи по сети SQL-команд, посылаемых серверу, и набора результатов, возвращаемых клиенту. Одно из основных достоинств этой конфигурации — простота. Программное обеспечение клиента посылает команда непосредственно ВД, чтобы отыскать или изменить то, что требуется. Исли эти команды хорошо продаманы и хорошо оформлены (тема, которую мы рассмотрим несколько глубже в главе 12), а результаты, которые мы получаем от сервера, настолько узки (настолько мало столбцов, насколько возможно) и настолько коротки (настолько мало строк, насколько возможно), насколько возможно, достижение требуемого результата будет обычно не слишком трудным. Если вы имеете относительно неволыпое число пользователей, которым разрешен доступ к вашим данным, эта конфигурация будет подходящей. Однако что означает "небольшое'’ число, будет зависеть непосредственно от типа аппаратных средств, на которых работает сервер ВД.
Когда число пользователей становится очень большим, или вы должны использовать слабую машину для управления вводом данных, непроизводительные расходы конфигурация системы клиент-сервер могут мать слишком большими, чтобы их допустить. Одна из главных причин в том, что наиболее дорогостоящие действия — соединение/разъедииение с сервером. Большинство приложений требует нескольких связей с сервером и, чтобы уменьшить непроизводительные расходы сети, они часто формируются так, чтобы постараться сохранить свои связи. Как мы сказали ранее, сохранение от пяти до десяти тысяч открытых связей сервера БД может привести к большому расходу ресурсов обработки данных. Трехзвенная конфигурация Конфигурации с тремя звеньями (и более общие п-звенные конфигурации) становятся очень популярными как способ отделить логику представления от бизнес-логики и бизнес-логику от логики данных. Как изображено на следующем рисунке, рабочая станция пользователя никогда непосредственно не связывается с сервером ВД, она просто соединяется с группой объектов (обычно написанных на языке, поддерживающем СОМ или CORBA), В идеале, бизиес-объекты, находящиеся на бизяес-уровие, имеют две очень важных задачи: Формирование пула соединений Чтобы уменьшить непроизводительные затраты модели клиент-сервер, объект-посредник объединяет соединения. Так как клиенты имеют тенденцию делать одни и те же запросы много раз, вместо того, чтобы открыть соедииенне с сервером БД на время, немного меньшее, чем обычно используется, объект-посредник формирует пул соединений, который он использует, чтобы соединиться с сервером БД. Вы также можете иметь много экземпляров объектов, кэшированных для использования в объекте-посреднике, а также иметь несколько объектов-посредников. По существу, философия, которая стоит за этой конфигурацией, заключается в том, чтобы забрать работу по соеданеншо с клиентом у сервера ВД и передать ее более масштабируемому устройству» Дальнейшие детали этого могут быть найдены в "Profeaswnal Window» РИА: Building Distributed Web Application» with VB, COM +, MSMQ, SOAP, and ASP" (Wrox Pram, ISBN 1МШМ4&1). Обеспечение бизнес-правил Мы можем легко столкнуться с проблемами функадгрнирования, связанными с конкретными бизнес-правилами, которые кажутся несколько произвольными и включают большое количество операций AND (И), NOT (НЕ) и ОН (ИЛИ), которые делают их неэффективными для кодирования. Примером такого правила могло бы быть следующее:
Подписка пользователя охватывает будние дни с панеделышка по пятницу, на НЕ субботы и воскресенья, если они не дают увеличение оплаты,-связанное с уикзнВами, хотя у панка не выходные дни. Если инженер требуется вне диапазона- ат 9 да 17 часов, ята потребует дополнительной оплаты, причитающейся ему за посещение. Правила, подобные атим, вообще не способствуют применению кода Traneaet-SQL, так как обработка будет требовать обратной связи от пользователя после первого же раза, когда он попытается сохранить запись, а правила подвержены частый изменениям я зависимости от потребностей изменения управления. Если мы кодируем на функциональном языке, способном взаимодействовать'с пользователями, бизнес-пра вила легче реализовать и, следовательно, изменять. Как мы увидим в.следующих двух главах, правила SQL Server должны управлять всеми неизменяемыми правилами, предписанными для данных в ВД, Сложное использование Web-серверов Другой сценарий, который становится все более распространенным, — наличие тысяч или даже миллионов пользователей, только читающих информацию, связанных с нашей ВД. В згой ситуации мы можем делать одну или большее количество копии наших данных только для чтения (в сильно денормализованнсй форм®). Переключатель, балансирующий нагрузку (и целая серия настроек, которые лежат за пределами моих основных способностей), берет запросы от клиента, направляет ати запросы на Web-сервер (каждый Web-сервер, являющийся копией одного и того же Web-eepsepa), далее Web-сервер вызывает один из серверов БД (снова управляемый переключателем, балансирующим нагрузку). Это показано на следующем рисунке: Нормализованная ВД — источник всех отредактированных данных, и если пользователь должен изменить данные (типа сделанного заказа-или сохранения некоторых настроек), то мы можем обеспечить интерфейсы и исходному серверу ВД или, возможно, другому серверу, который мы используем только для редактирования данных. Возьмем, например, большие медиа-носители информации и электронные коммерческие сайты книг е десятками тысяч CD, книг и т, д. Вся информация относительно этих элементов будет находиться в нормализованной ВД, сконфигурированной так, чтобы обеспечить целостность данных.
Учитывая особенности функционирования, мы могли бы взять mt данные иа своей нормализованной формы в преобразовать их с помощью предварительно построенных, предаарительво соединенных запросов, чтобы создать Web-страницы, которые будут видеть клиенты Web-сайта, Мы могли бы также разделить данные на серверы. На основе факторов типа частоты использования мы могли вы поместить классическую музыку аа один сервер, рок-музыку на другой, и так далее. Глобальная сап В вашей глобальной экономике многие организации расположены по всей стране или даже на разам* континентах. В некоторых случая* мы можем построить простой клиент-сервер или даже многозвенные приложения, которые будут достаточны для наших нужд редактирования данных. Однако есть много случаев, когда непрактично редактировать и передавать данные по глобальной сети. Следовательно, мы можем построить топологию, которая в основном выглядат следующим образом: Мы можем использовать тиражирование слиянием, чтобы позволить редактировать на любом конце канала, а затем сливать изменения назад во все серверы. В этом случае каждый из офисов на нашем рисунке будет чувствовать, как будто он имеет свою собственную ВД, где он может редактировать все строки в ВД. Мы могли вы разделить данные, чтобы избежать ситуаций, когда один пользователь в штате Теннесси редактирует какую-то строку, в то же самое время другой в Лондоне делает то же самое, но мы могли бы просто использовать средства разрешения конфликтов SQL Server для управления ситуациями, когда они возникают. Учебный пример В нашем учебном примере мы собираемся использовать самую простую из возможных топологий, в то же время отобразив все идеи, которые мы рассмотрели. Мы будем использовать простую установку клиент-сервер с дополнительным-оперативным хранилищем данных:
Одна из главных причин придерживаться испытанной и надежной топологии клиент-сервер заключается в том, то мы будем реализовывать бизнес-правила на сервере БД, когда это возможно. Мы, конечно, обсудим любую ситуацию, когда эта топология клиент-сервер является помехой. Последняя вещь, которую следует упомянуть относительно нашего решения использовать структуру клиент-сервер, — независимо от того, какую топологию нужно реализовать, следует обычно иметь как можно больше правил, помещенных на сервер ВД, по причинам, которые мы опустим. Мы реализовали их здесь как пример, и именно так я построил бы систему. Резюме Как вы, наверное, догадались, познакомившись с предыдущими главами этой книги, абсолютно нет НИКАКОГО простого ответа, как выть с чрезвычайно широким диапазоном возможных ситулни й/конфи гураций /параметров, которые могут затрагивать реализацию. Очень важно, чтобы вы использовали не только то, что вы узнали в этой главе, но, в зависимости от вашей роли, изучили бы все, что мажете изучить относительно того, как работами1 SQL Server и его дополнительные средства, используя как другие книги (некоторые из них мы упомянули в тексте), так и SQL Server Books Online. Мы рассмотрели маленький подраздел различных возможных способов того, как связать ВД с клиентом или как выбрать структуру решения. Данная глава не дает вам ничего, что вы могли бы почерпнуть относительно физической структуры проекта ВД. Это потребовало бы целой книги само по севе. Что мы стремимся сделать — дата вам основную часть информации, которая позволить прочувствовать предмет и работать с ВО процентами проблем, с которыми вы столкнетесь, и решить, где найти дальнейшую информацию, чтобы бороться с оставшимися 20 процентами проблем. Следующая глава возвращает нас назад на путь, где мы более глубоко посмотрим вл процесс создания таблиц, включая полное рассмотрение основных объектов SQL Server, таких как ивдексы и типы данных, которые мы будем использовать, чтобы создавать ваши окончательные ВД.

Планирование и реализация основной физической структуры Введение Наша цель в то» главе -— построение таблиц, которые составят. :иашу БД. Мы-говорили относительно этого процесса, планировали его, даже рисовали картинки, ио теперь настало время реализовать.все физически. Процесс создания OLTP-таблиц может быть в раде случаев трудоемким, ио, вели вы используете любое иа известных средств проектирования БД, задача будет гораздо более легкая. На стадии реализации мы должны реорганизовать наши объекты, добавив практические детали и, наконец, преобразовать это все и код. В этой главе мы будем строить наш код без иепоиьзоваяия каких-либо средств проектирования или генераторов кода, «к что вы сможете точно увидеть, что- происходит. Вместо этого мы будем использовать для создания таблиц Query Analyzer (средство выполнения SQL-заироеов) и ручное редактирование на языке оадздедеяия данных (Data Definition Language — DDL). Ми рассмотрим код формирования таблиц и отношений, но не код определения доменов данных, которые используются для выбора типов данных. Мы исследуем в некоторой степени каждый из внутренних типов данных, обеудим отдельные расширения их, которые могут нам потребоваться, и определим некоторые основные столбцы и типы дан ных, которые будем, вероятно, неоднократно использовать» .Следующая глава будет посвящена уточнению определения доменов, чтобы в дальнейшем ограничивать значения данных конкретными размерами или использовать их в задачах определения типов.
Мы определим уникальные ограничения и иидексы для наших таблиц и рассмотрим физические структуры расположенных ва диске индексов и таблиц. Основы понимания, как размещаются индексы и данные, помогут строить представления того, что происходит внутри SQL Server при размещении данных и выполнении запросов, В свою очередь, это поможет нам получить ясную картину, в каких индексах мы нуждаемся при тех или иных ситуациях. Когда мы завершим рассмотрение тем этой главы, то получим схему ВД, включающую группу таблиц с индексами и отношениями. Наша БД будет далека от завершения, но мы создадим прочную основу для реализации бизнес-правил, которые будут рассмотрены в следующей главе. Средства генерации БД Чего нам следует придерживаться на практике — никогда не создавать БД без приличного программного средства. Имеется несколько превосходных средств формирования ВД, которые также хороши и для моделирования ВД, Создание сервера ВД возможно и без них, так как имеется ряд администраторов ВД, которые имеют множество текстовых файлов, представляющих их ВД, но они расточительно тратят время. На этой стадии реализаций’мйогйе программисты будут стремиться начать писать код. Если мы начнем создавать все наши таблицы и код вручную, это потребует для выполнения - огромное количество времени. Первая система, для которой я написал код БД, включала более чем 800 хранимых процедур и триггеров, написанных вручную более чем за восемь месяцев, Это было восемь лет назад, и это был фантастически поучительный пример. Сегодня современные средства проектирования ВД позволяют мне строить БД с сопоставимым кодом в течение недели. Имеются средства, которые выполняют для нас многие "детали" работы, но если мы не вполне понимаем, как они делают, ото, очень трудна обеспечить их успешное применение. Выполнение этих ’‘деталей'’ вручную перед использованием таких мощных средств будет стоить крови, пота и слез. Однако чтобы показать вам, как работает процесс физической реализации, мы не будем использовать никакие из этих средств в течение следующих трех глав. Мы будем строить вручную сценарии, которые средства проектирования сформировали бы автоматически. Это поможет нам понять то, что мы получаем с помощью таких средств. Оно является также хорошим упражнением для любого архитектора БД или адмиииетратора ВД чтобы рассмотреть синтаксис SQL Server; только не делайте это m ВД с девяноста таблицами, если у вас нет в запасе восьми месяцев. В этих главах мы будем иметь дело, прежде всего, с разработкой БД, Разработка ВД связана с размещением данных, которые не имеют никакой деловой ценности, Разработчики должны быть способны изменять или удалять любые злемеаты данных (но не структуры), разрабатывая код доступа к БД. К сожалению, многие программисты будут сразу же просить реальные даиные для ВД.
Схема физического проектирования В части книги, посвященной логическому проектированию, мы обсуждали весьма детально, как проектировать наши объекты, В этом разделе мы будем брать этот проект и разрабатывать на его основе реальные объекты ВД, Так же как ин жен еры-конструкторы (эерут чертежи архитектора и исследуют их, чтобы разобраться в том, какие необходимы материалы и является ди все это реализуемым, мы возьмем налгу логическую модель и выделим те части, которые могли вы быть иереалиауемы или слишком трудны для реализации. Некоторые части первоначального представления архитектора, вероятно, врвдется изменить, чтобы сделать его реалистическим на основе факторов, которые не были известны, когда проект выл первоначально задуман, наподобие типа почвы, наличия холмов, и.т, д. Мы также окажемся перед необходимостью выполнить процесс перевода представлений из сырой вовдеттции в реализацию. В течение логического проектирования мы поверхностно смотрели аа любые реальные примеры кода и старались рассматривать только особенности, обжив для всад реляционных СУБД. Логическая модель, которую мы разработали, могла вы выть реализована и в Microsoft SQL Server, и в Microsoft Асееве, и в Oracle, и в Sybase и в любой другой СУРБД, Начиная с этого момента, я буду считать, что вы установили Microsoft SQL Server 2000 и создали рабочую ВД, Мы рассмотри?.! многие из особенностей, которые SQL Server дает мам, хотя и яе всей Для получения большего количества информации относительно Microsoft SQL Server 2000 читайте "Professional SQL Server 2000 Programming9, f?ob Vieira (Wrox Press, ISBN 1-887004-48-6 }. На протяжении всей главы вашей целью будет преобразование логического проекта в физическую реализацию, настолько близкую, насколько возможно. Мы рассмотрит,! следующее: О преобразование логического проекта в физический проект; □ создание таблиц; □ создание столбцов; □ типы данных; □ служебные столбцы; □ сопоставление; □ ключи; □ создание отношений; □ распределение ваших метаданных между разработчиками. Каждый из этих шагов будет подровно рассмотрен, чтобы получить надлежащее понимание того, что в основном доступно для получения реального проекта и правильной реализации. Преобразование нашего логического проекта Раньше раздел, подобный этому, посвященный физическому проектированию, был бы намного более длинным, чем те немногие страницы, которые мы имеем здесь. Как мы уже говорили, потенциальные возможности аппаратных средств и программаого обеспечевия, совместно с методологиями хранилищ данных, потенциально позволяют нам реализовать БД почти точно в соответствии с логическим проектом. Однако нужно быть осторожным при проектировании физической ВД, чтобы не попытаться реализовать что-то, что является слишком трудным для практического использования.
Подтипы , Нам, вероятно, придется отклониться от нашего логического проекта, когда имеем дела а подтипами. Вспомните, что подтип в терминах моделирования ВД указывает определенный тип отношений "одна к одному", где мы имеем одну основную таблицу, например,person (человек), и одау или более таблиц вадтипов, которые уточняют значения этой таВшщы, например, employee (служащий), teacher (преподаватель), customer (заказчик) и т. д. Они особенно существенны в логическом проектировании, ио имеются также серьезные основания, чтобы сохранять их и s нашем физическом проекте. В зависимости от того, как вы завершили ваше логическое моделирование, вам, вероятна, придется выделить дапалнителъные подтипы. Всегда имейте в виду, что имеется столько путей проектирования ВД, сколько существует архитекторов данных. - Я приведу примеры случаев, когда мы будем или не будем хотеть иметь таблицы подтипов. Пример 1 Пусть мы имеем таблицу movie (кинофилам), которая содержит названия кинофильмов {наряду е другой информацией тика жанра, описания и т._д.). Мы также имеем таблицу movieRentallnventoxy (каталог арендной платы кинофильмов), которая определяет ареедаую плату за видеокассеты или DVD. Зададим число, которое используется как. вторичный ключ, и столбец itemCount (количества), определяющий число видеокассет или дисков, которые находятся в пакете арендуемых фильмов, Имеется также столбец mediaFormat (формат фильма), который определяет, является ли фильм видеофильмом или DVD. Movie Машей Name MovieRentallmentay MovieRentalliwentoryH | Movield (FK) “~| '• Number (AKI и : ItemCount I MediaFormat ; MediaFormat VideoTape I MovieRen’aHrwentoryfd (FKJ I HiFiSoundFI ] S; er eo Sound) i - J- DVD (MovieRentallrweritoryld (FKJ \ RefflonEncoding i Versine - j 4AspectRatto ) Sountfonnat \ ScwndFematM Nara (AK1.1) Description DVDSoundFormat DVDSouindFormattd • SouncFormatld фКДАК' 1) MwieRentaltaentoryld (FK}(AK1,2) _________________________________I
Здесь Movie — таблица- "кинофильм"; M&vield — идентификатор кинофильма: — название (кинофильма); MovieRentalInventory — таблица "каталог арендной- платы за кинофильмы"; MovieRentailnventoryld — идентификатор арендной платы за кинофильмы; Number — номер элемента каталога; TtemCount — числа элементов; MediaFormat — формат фильма; VideoTape — таблица “ видеокассета”; HibiSoundFl — флаг звука высокого качества; StereoSoundFl — флаг стереозвука; dvd — таблица "DVDduex”; RegionBncoding — над региона; Version — версия; AspectRatio — форматное соотношение; 'SovndFormat — таблица "формат звука"; Sens nd Format I'd — идентификатор формата звука; Description — описание; DVDSoundFormat — таблица "формат- звука DVD"; DVDSaundFormatld — идентификатор формата звука- DVD, Мы определяем подтипы значения ntediaFormat для videc-Tape и DVD. Нам нужно знать формат, потому что DVD имеют намного больше особенностей, чем видеозаписи, наиболее важными из которых являются код региона и формат звука. Мы реализуем специальный DVDSoundFormat для DVD как отношение "многие ко многим", так как DVD поддерживают любые наборы форматов звука; в то время как для типа videoTape мы имеем только ноля для высококачественного и стерео звука. Имеется несколько проблем с этой реализацией: □ Чтобы сформировать новую арендную плату за видеофильм, мы должны создать записи, по крайней мере, в двух таблицах (mcvieRentallnventory и videoTape или DVD, и еде в DVDSoundFcznat для DVD), Это — не слишком большая проблема, но может потребовать некоторой специальной обработки. □ Если мы хотим видеть список всех элементов каталога арендной платы, включая звуковые характеристики, мы должны будем написать очень сложныйзапрос, который присоединяет novi eRentai-in venter у к videoTape, и объединить с другим запросом между яочд. ©Rental Inventory-»DVD, что может быть достаточно медленно или просто слишком тяжело для реализации. (Ми детально рассмотрим запросы в главе И.) Это общие проблемы с подтипами. Что мы должны решить — это насколько целесообразно держать данный набор таблиц, реализованных как подтипы. При радаютреийи того, что делать с отношением подтипов, одна из наиболее важных задач, которую нужно решить, — сколько будет в общей сущностей в каждом подтипе и сколько атрибутов они в общем имеют. Простой обзор атрибутов таблиц подтипов в атом примере-показывает, что они имеют много общего, В большинстве, случаев это — просто допрос количества элементов. Возьмем DVD. Код региона характерен для этого видеофильма, во некоторые видео имеют различные версии, и технически каждое изделие видео имеет форматное отношение >= = (отношение высоты и ширины изображения), которое будет интересно любому энтузиасту. Поскольку мы можем определить характеристики звука видеозаписи, используя два простых выключателя, этот же самый метод определения звука мы можем использовать для DVD. Мы будем только должны ограничить количество элементов soundforeat на видео одним, в то время как DVD может иметь их до восьми.
Таким образом, мы изменяем отношения подтипов следующим образом: Movie Movietd Name SoundFormat SoundFormatld Name (AKI. 1) Description ItaleRantattrwentory MovieRentaflrwntoryid 1 i MovieId (F KJ • Number (AKI Л> Version item Count Media Format AspectRatfo DVDRegiortEncQdmg MowRentaBrwantorjSowMiformat- ! MwieRentaflrwentdrySour^ftjrmatid ''*l MovleRentallrwentoryld (FKKAK1 .1) : SoundFormatld (FK)(AK1.2) Здесь дополнительно MovioRentalloventaxySoundFarnat — таблица "формат звука каталога арендной платы за кинофильмы"; MovieRentalInventorySoundForiaatld — идентификатор формата звука каталога арендной платы, за кинофильмы. Обратите взимание, что это нарушает правили нормализации, Од1-ако действительно трудно объединить использование подтипов, которые не нарушают нормализацию, если два объекта, являющиеся подтипами, содержат в основном одни и те же составные части. Последний момент, который должен быть сделан, связан с тем, что поскольку мы изменили нашу структуру и храним различные типы информации в одной и той же сущности, то теперь должны быть осторожны, чтобы не допустить ввода в таблицу неподходящих данных. Например, когда aediaFonaat — видеокассета, то поле DVDRegionEnocding не используется и должно Выть установлено в HULL. Эти бизнес-правила будут реализованы, используя ограничения и триггеры, так как значения в одаой таблице будут определяться значениями в другой. Может показаться, что сворачивание (или объединение) подтипов в единственную таблицу дает более легкую и более прямую реализацию, в то время как фактически облегчение работы возможно лишь где-нибудь еще. Знание, какие значения используются, конечно, поможет нам решить, стоит ли сворачивать подтипы. Пример 2 В качестве второго примера рассмотрим случай, когда не имеет смысла сворачивать подтипы. Вы будете обычно иметь подтипы, которые не стоит сворачивать, когда имеются два (или больше) объекта, которые делят общего предка, но, являясь подтипами, эти подтипы не имеют абсолютно никакого отношения друг к другу. Посмотрим на следующую структуру:
Person [ Employee ’________ Persanbd (F К] Function Number V ........: Customer. i , Personld (FK) | Number -- । AccountMumber : У';? " ' r Здесь Person — таблица "человек": Fersanld — идентификатор человека;- type — категория; Name — имя; ЕярЛоуае — таблица "служащий"; Function ~ должность; Number — номер; Customer — таблица "заказчик”; AccountWuatber — номер счета. Мы имеем таблицу person, которая содержит имя и другие атрибуты, включая такие, которые реализованы как дополнительные таблицы с отношениями к та&шцецоахзэд (такие как адрес, информация о партнерах, журнал партнеров и т. д.)> В нашем примере и служащие, и заказчики являются людьми, но сходство на этом и завершаемся. Поэтому в данном случае лучше оставить подтипы, как- они есть. Единственная проблема здесь состоит и том, что если кто-то хочет узнать имя служашего, вы должны будете использовать соединение. Это не является проблемой с точки зрения программирования, но это проблема с технической точки зрения. Во многих случаях основные особенности, которые определяют подтипы, более разумно испоМзовать в создаваемых представлениях, которые позволяют рассматривать таблицуemployee как будто она содержит также и параметры таблицы person. Определение, когда следует сворачивать таблицу — один из тех случаев, который приходит со временем и с опытом. Как правило, обычно лучше следующее: Q Свериуть, когда таблицы подтипов очень похожи характеристиками на таблицу-предка, особенно, когда мы часто хотим видеть их в- списке -вместе. Q Оставить в качестве подтипов, когда данные в подиимх разделяют общие характеристики, но логически не связаны друг е другом каким-либо образом.
Другие причины отхода от логического проекта Наиболее частая причина отхода от логической модели — денормализованные структуры, обычно итоговые данные, суммирующие заданны# набор строк, как в следующих таблицах: Acccjrt TransacUon Accojr;ld Numbe- -------- ------[ Balance i ТгагиасвапИ Aoco jriid ;FK! -d Numbs' Amount Здесь Account — таблица- "счет"; Account Id — идентификатор счета; А’шигег — номер; Balance — баланс; Transaction — таблица "сделка"; Transactianlti — идентификатор сделки; Amount — сумма. Здесь атрибут balance будет эквивалентен итогу всех сумм сделок для данного account Id. Мы обсудим, как реаллзовать этот тал итоговых данных, используя триггеры, так как иногда может потребоваться введение денормализоваыиых даяиых, чтобы решить конкретные задачи функционирования. Введение этого на ранних стадиях физического проектирования может показаться разумным. Однако следует подчеркнуть, что деиормвлизацию не следует выполнять, если нас не вынуждают обстоятельства, Обычно денормализация причиняет больше проблем, чем их решает. Для архитекторов-новичков может быть тяжело понят», почему лучше восетавовмь значения, используя соединения вместо создания дубликатов значений в ВД, чтобы ускорить запрос или группу запросов. Объединения в SQL Server чрезвычайно эффективны и очень часто обеспечивают адекватное функционирование. Таблицы Таблицы являются центральными объектами в SQL Server. Основы таблиц очень просты. Если вы посмотрите на полный синтаксис для оператора create TABLE, то увидите, что имеется много различных необязательных параметров. В этой главе и части следующей мы рассмотрим оператор создания таблицы и как лучше всего выбрать, что делать с каждой разновидностью. Первый необходимый момент очень прост: | CREATE TABLE [<ВД>.)(<влалелец>.]<имя_тавлицы> Мы будем добавлять элементы в угловых сковках (<дополнительный_алемент>). Обратите внимание, что выражение в квадратных сковках ([дополнительный_элемеит]) является необязательным и может быть опушено. □ <ЗД> — обычно вам не нужно определять ВД в операторе CREATE TABLE. Если она не определена, подразумевается текущая БД, где выйблняетея оператор. □ <владелец> — это имя владельца таблицы. Им должен быть текущий пользователь или dbo — владелец ВД. Если оператор выполняется лицом из категории системных администраторов (sysadmin) сервера, имеющим функции либо владельца ВД (db_dbowner), либо администратора данных (db_ddladmin), то таблица может быть создана для любого пользоватем путем задания его имени.
Лучшая практика — минимизация чисм владельцы. Наличие многих владельцев сведает проблемы "цепи владельцев" между зависимым» объектами. Обычно все объекты принадлежат только одному пользователю, имеющему имя clba (database оизпег — владелец ВД). Обозначение Значение л«я_таблицы> да» имя таблицы. Имя должно соответствовать типу системных данных sysname. В SQL Server он овдедаин как стрижа из 128 двухбайтовых символов Unicode. Сочетание owner (владелец) и cablename (имя таблицы) должно быть уникально для БД. Если первый символ имени таблицы — одинарный символ 9, таблица является временной локальной. Если первые два символа имени таблицы — ##, эти — глобальная временная таблица. Мы кратно упомянем временные таблицы в главе 12, хотя они не такая уж большая часть проекта БД, как механизм размещения промежуточных результатов в сложных запросах. Правила SQL Server для имен объектов состоят из двух разных методов обозначения. Первый исползует ограничители (или квадратные скобки, или двойные кавычки) вокруг имени (хотя двойные кавычки допусттаы только тогда, когда установлена опция SET QEJOTED_IDENTTFISR — задание идентификатора в кавычках). Помещая ограничители вокруг имени объекта, вы можете иеподазовять в качестве имеет лю^ю строку. Например, [Table Name] или [3232 fjfa*S(fiA(] были бы оба законными (но раздражающими) именами. Использование имен с ограничителями вообще-то плохая идея при создании новых таблиц, и его нужно по возможности избегать, поскольку оно делает кодирование бодае трудным. Однако оно может быть необходимо для взаймодействия с таблицами данных в других средах. Второй и предпочтительный метод обозначения состоит в том, чтобы использовать имена бее ограничителей, но они должны следовать нескольким основным правилам: □ Первый символ должен быть символом, определенным в Unicode Standard 8.1 (короче говоря, латинские символы от А до Z верхнего или нижнего регистра), или символом подчеркивания ( ). Стандарт Unicode может выть найден в www.unicode.arg, □ Последующие символы могут выть буквами Unicode, десятичными цифрами, амперсандом (@) или знаком долларе. ($). □ Имя не должно выть зарезервированным словом SQL Server. Имеется довольно большой список зарезервированных слов в электронном руководстве по SQL Server 2000 (смотрите раздел Reserved Keywords — зарезервированные ключевые слова). □ В имени ие должно быть пробелов. В то время как правила создания имен объектов достаточно очевидны, более важный вопрос — ’Какие имена мы должны выбирать?" Ответ предсказуем: "Независимо от того, что вам кажется, лучшее — это когда другие могут их прочитать." .Это может звучать как попытка уйти в сторону, но имеется столько много различных стандартов обозначения, сколько существует архитекторов данных. Стандарт, которого я обычно придерживаюсь, — стандарт, который мы будем использовать в этой книге, но это, конечво, не единственный способ. В основном, мы будем следовать тому же стандарту, который мы начали использовать в логических именах. Учитывая размер в 128 символов, есть немного причин делать много сокращений. Это наиболее важао понять при выборе имев.
Так как болыиинства кампаний имеет уже существующие системы, неплохо бы. узнать стандарт предприятия для обозначения, чтобы новые разработчики вашего проекта лучше понимали вашу БД и быстрее вошли в ритм. Как пример, дадим имя объект у, который мы могли вы использовать, чтобы хранить телевизионную программу. Следующий список даст несколько различных способов сформировать имя этого объекта: □ television_schedule_item (телевизионная программа) — использование символов подчеркивания для отделения различных слав. Большинство программистов не очень любят символы подчеркивания, поскольку они непривычны в программе, пока вы не свыкнетесь с ними. □ [television schedule Item} или "television schedule item"—ограничение сковками или кавычками, Не одобряется большинством программистов, поскольку невозможно использовать это название при задании переменных в коде и легко допустить ошибки. televisionScheduleltem — смешанный случай для разграничения слов. Это — стиль, который мы используем в нашем учебном примере, так как он мне нравится. □ tvSchedltem или tv_Bched_iteni или [tv sched item) —сокращенные формы. Они проблематичны, потому что вы должны быть осторожны, чтобы всегда сокращать одно и то же слово одинаковым образом во всех ваших БД. Необходимо вести словарь сокращений, или вы получите различные сокращения для одного и того же слова, напрюдар, description (описание) как. desc, descr, descrip, и/или description. Выбор имея для объектов, в конечном счете, — личный выбор, но никогда это не нужно делать произвольно, и он должен быть основан на существующих общих стандартах, существующем программном обеспечении и доходчивости. Пользователь, который создает БД, называется владельцем (owner или dbo) ВД Владелец (dbo) — один из администраторов системы или системных специалистов, создающих ВД. Любой пользователь может быть помещен в группу владельцев БД, добавляя функцию db_owner (владелец БД), означающую, что пользователь может создавать и удалять любые данные, таблицы, представления и хранимые процедуры. Когда, любой пользователь, нв-владвлец, создает объект, этот объект принадлежит только атому пользователю (и, конечно, dbo или db_owner). Вели пользователь пр имени Бей пробует получить доступ, к таблице без того, чтобы задать владельца примерив так; | SELECT * FROM tableHaine ... то SQL Server сначала проверяет, существует ли таблица, названная tableHaae и принадлежащая на самом деле Бобу. Если нет, то проверяется, существует ли таблица с этим именем, принадлежащая dbo. Бели это так, но dbo не дал Бобу нрава доступа к таблице, оператор выдает ошибку. Для доступа к таблице конкретного владельца непосредственно в коде мы должны определить владельца таблицы, используя ’две части имени, примерно так: Ц SELECT * FROM dbo. tableHame
Если мы определим конкретную таблицу и ее владельца, как сделали в предыдущей строке кода, а эта таблица не существует, возникнет ошибка. Владелец получает неявные права создавать, изменять и удалять данные иа таблицы, как и саму таблицу. Любому другому пользователю нужно давать права делать это. В данный момент просто важно понять, что каждая таблица, представление и хранимая процедура в SQL Server имеют владельца, Мы должны также обратить внимание, что SQL Server использует схему имен, включающую до четырех частей: (<сервер>.) [<ВД>.J[<владеле«>.]<имя_об'ьекта> где: □ сервер — определяет сервер, на котором расположен объект. Для объектов на том же самом сервере имя сервера должно быть опущено. К объектам на других серверах можно обращаться только в том случае, если администратор системы задал сервер как связанный. □ ВД — определяет ВД, где находится объект. Если эта часть опущена, объект разыскивается в ВД, из которой к нему обращаются. □ владелец — определяет иолыоиателя, кто создает/владеет объекте ом). Если эта часть опущена, SQL Server использует пользователя, который связан с объектом по умолчанию. □ имя объекта — определяет имя таблицы, представления или хранимой процедуры. Ограничения Перед тем, как идти дальше, мы должны рассмотреть некоторые ограничения, с которыми должны будем работать при создании таблиц в SQL Server. При создании таблиц для нас важны четыре ограничения: □ Число столбцов — Максимальное число столбцов (то есть полей данных) в таблице — 1024. Если ВД должным образом нормализована, это за границами обычных требований. □ Число байт на строку — Максимальное число байт в строке — 8060. В. отличие от числа столбцов максимальное число байт не так уж трудно достичь. Мы увидим это, когда начнем рассматривать символьные и двоичные данные. □ Число байт н* вход индекса — Предел *— 900 байт, и для втого есть серьезные основания. Страница содержит примерно 8000 байт, так что если бы индекс был намного больше, чем 900, он стал вы очень неэффективным. □ Число индексов — Предел — 1 кластерный и 249 иекластерных-. Мы могли бы подойти близко к этому пределу в системах хранилищ данных. Наличие 250 индексов таблицы в OLTP-системе существенно уменьшило бы эффективность работы .при частых корректировках, которые, вероятно, будут производиться е данными. Мы обсудим кластерные и некластерные индексы позже в агой главе. Более полный список основных требований будет дан в главе 1В, когда мы рассмотрим требования к аппаратным средствам.
Столбцы Строки, выделенные полужирным шрифтом, являются строками, которые мы используем для определения етолвиа: I CREATE TABLE [<ВД>,1f<алалелец>.1<имв_таВлицы> < <имж столбца» <ти11_даиы11х> [«спецификация NULL»] ' — или <имя_столбца> AS <вичисленное_значение> ! Имеются два типа столбцов, с которыми мы будем иметь дело, — физический и вычисленный (или виртуальный). □ Физически® столбцы — это обычные столбцы, где реализовано физическое хранение, и данные фактически содержат значения. □ Вычисленные (или виртуальные) столбцы — столбда, которые получаются в результате вычислений, включающих значения любого из физических столбцов таблицы. Мы, прежде всего, вудем иметь дело с физическими столбцами, хотя вычисленные столбцы имеют некоторые симпатичные евежже использования. Обозначение Значение <имя_столбца> — это то, где вы определяете имя, под которым будет известен столбец. Правила обозначения столбцов те же самые, что и для таблиц, и связаны с SQL Server. Что касается того, как мы выбираем имя для столбца — снова, это одна из тех задач для конкретного архитектора, оенованная на тех же самых видах критериев, как и прежде (стандарты предприятия, лучшее использование и т. д,). В агой книге мы будем следовать такому перечню основных принципов: □ За исключением первичных ключей, имя таблицы не стоит включать в имя столбца. Например, в сущности по имени person (человек) мы не должны иметь столбцы, называемые регволМате (имя человека) или persanSocialSecurityJhHnber (номер синельного обеспечения человека). Никакой столбец ие должен иметь в качестве префикса person за исключением первичного ключа person Id. Это уменьшает потребность в функциональном имени (изменяющем имена атрибутов, чтобы корректировать смысл, особенно используемом в случаях, когда мы имеем много мигрирующих внешних ключей). □ Имя должно быть настолько описательным, насколько возвеожяо. Мы будем использовать очень немного сокращений в наших именах. Имеются три известных . исключения: Сложные имена — Так же как и в именах таблиц, если вы ймеете имя, которое содержит несколько частей, например, '’Conglomerated Television Rating Scale" (комплексный показатель рейтинга ТВ-передач), вам хотелось вы иметь название вроде ConTvRatScale, даже если потребуется некоторая тренировка, прежде чем ваши пользователи привыкнут к его значению.
Признанные сокращения — Например, если бы мы писали систему закупок и нам нужен бы был столбец для таблицы заклав, мы могли бы назвать объектFQ (purchase order — заказ), потому что это очень широко используется. Указатели типов данных — Мы будем иногда добавлять короткую строку (скажем, два символа) в конец имени столбца. Например, столбец, который мы используем как Boolean, будем завершать суффнкеом "fl” (сокращение от flag — флаг), а столбец даты, заканчивающийся суффиксом “di”, очевидно, будет означать сокращение даты. Заметьте, что мы не будем исподазовать эти суффиксы, чтобы .указывать точные пшы данных (например, вы могли бы реализовать тип Boolean е типами int, tinyint, smallint, bit- или даже string, содержащей "Да" пли "Нет"). Обратите внимание, что мы не упоминали о Венгерской нотации, чтобы обозначить тип столбца. Я никогда не был большим поклонником этого стиля. Если вы не знакомы е Венгерской нотацией, она означает, что мы используем щюфиксы имев столбцов и переменных, указывающие тип данных и возможное использование. Например, мы могли бы иметь перемениую, вазьшаемую vcIOO_columnName, что указывает тип даввых varchar (100). Или мы могли бы иметь столби! тиа Boolean или bit, который назван bear или isCar. По моему мнению, такие приставки — массовое самоубийство, так как легко взять тип из другой документации, которую мы можем получить из SQL Server или другими методами. Наши индикаторы использования обычно идут в конце имени и необходимы только тогда, когда было бы трудно понять без них, что означает конкретная величина. Храня точный тип имен, мы не будем омрачать детали реализации идентификацией сущностей. Одна из красивых сторон использования реляционных БД— что имеется уровень абстракции, который скрывает детали реализации. Отображать их через обозначение столбца означает сделать их стабильными, хотя требования модернизации могут привести к тому, что они устареют (например, расширяя размер переменной, чтобы приспособить к будущим деловым потребностям). Домены В логическом моделировании концепция доменов включала построение шаблонов для типов данных и столбцов, которые шл используем многократно. В физическом моделировании домены — те же самые, но о дополнительными свойствами, добавленными с точки зрения физических потребностей. Например, на логической стадии моделирования мы определили домены дяМ таких столбцов, как пате (имя) и description (описание), которые возникают регулярно на протяжении всей ВД. Причина определения доменов полностью не могла быть понятна на стадии логического проектирования, но становится совершенно- ясной иа стадии физического моделирования. Например, для домена пате мы могли бы определить следующее: Свойство Значение Имя Тип данных Возможность значения NULL Проверка ограничений Name Varc-har (100) Not NOLL LEN(RTRIM(Name)) > 0 ~ не может выть пустым 10-1868 271
Большинство таблиц будет иметь столбец наше, и жа будем использовать данный шаблон для его построения. Это преследует, по крайней мере, две цели; □ Последовательность — если мы определяем каждый столведпате одним и тем же образом, никогда не возникнет вопрос, как обращаться со столбцом, □ Простота реяли-зйияя — если средство, которое вы используете для модажирования/реалияапии ВД, поддерживает создание доменов/шаблонов столбцов, вы можете просто использовать шаблон, чтобы настроить столбцы, и не должны многократно устаяавдавать значения. Вели средство поддерживает наследование свойств, то когда вы изменяете свойство в определении, значения изменятся всюду. Домены не являются необходимостью хорошего проекта БД, логического или физического, и не должны использоваться в обязательном порядке в SQL Server, но они позволяют легко и последовательно проектировать, что само по себе является хорошей идеей. Конечно, последовательное моделирование — всегда хорошо, независимо от того, действительно ли вы используете программное средство, которое делает работу для вас. Выбор типов данных £ <имя_стол6ца> данных» |<спвцификация_№Ы->] Выражение <тии_дамных> используется для выбора типа данных столбца. Выбор надлежащих типов данных, чтобы соответствовать домету, определенному на стадии логического моделирования — очень важная задача. Один тип данных мог бы быть более эффективен, чем другой, подобный тки. Например, размещение целых данных можно сделать е иепользаваняем типа целых данных, числовых данных или даже е плавающей точкой, но они, конечно, не эквивалентны в реализации или функционировании. В этом разделе мы рассмотрим все встроенные типы данных, которые обеспечиваются фирмой Microsoft, и обсудим ситуации, где их лучше использовать, Точные числовые данные Имеется много вазовых типов данных, в которых вы можете хранить числовые данные. Мы рассмотрим две различные категории числовых данных: точные и приближенные. Различия очень важны и должны быта, хорошо иониты любым архитектором, кто создает систему, которая хранит покаваяжя, измерения или деугив числовые данные. Точные числовые значения включают типы bit, int, bigint, sraallint, tinyint, decimal и денежные типы данных (money и smallmoney). Точные значения не имеют никакой ошибки, как бы они ни были размещены — в виде целых чисел или чисел с плавающей точкой, потому что они имеют фиксированное число цифр до и после десятичной точки (или другой системы счисления). Однако когда мы должны хранить десятичные значения в точных типах данных, мы заплатим за работу с ними тем, что будем должны указать, как они хранятся и как с ними работать.
bit Тип bit (вит) имеет значения 0, 1 или HULL, Он обычно исиольауется кам тип Boolean, Это — ие идеал типа Boolean, потому что SQL Server не имеет отдельного типа данных Boolean, ио его — лучшее, что мы в настоящее время имеем. Столбцы типа bit не могут быть индексированы — наличие только двух значений (фактически три с BULL) приводит к неэффективному индексу, так что он не должен использоваться в сложных поисковых комбинациях полей. Если вовможно, вы должны избегать значения NULL в полях типа bit; включение значений 'Ji'LL в логическое сравнение увеличивает сложность запроса и вызывает проблемы. Столбец типа bit требует одного байта для хранения восьми екземпляров данных -таблицы. Следовательно, наличие восьми столбцов типа bit приведет к тому, что ваша таблица будет не больше, чем если бы она имела только один столбец типа bit. int Целые числа от -2 147 488 648 до 2 147 483 847 (то есть от 2SI до 231 ~ 1). Тип данных int (целое число — можно также использовать идеятшрикатор integer, прим, перев.) используется, чтобы хранить знаковые (+ или-) целые числа. Тип данных int часто используется как первичный ключ для таблиц, поскольку он рчея» мал (требует четырех байт хранения) и очень аффективен для хранения и извлечения. Единственный реальный недостаток типа данных int состоит в том, что он не включает беззнаковой версии, которая хранила бы неотрицательные значения от О до 4294967296 (шж 2а2), Поскольку большинство значений первичного ключа начинается с 1, это дало бы нам более двух миллиардов дополнительных значений для первичного ключа. Это может казаться ненужным, но системы, которые имеют миллиарды строк, становятся все более обычными, Другое приложение, где поле типа int играет важную роль — хранение IP-адресов как целых чисел. IP-адрес — просто 82-битовое целое число, раввитое на четыре октета. Например, если вы имеете IP-адрес 284.23.46.12В, то можете ваять (284 * 256*) + (28 * 256*) + + (45 * 2581) + (123 * 256°). Это значение будет хорошо подходить под 32-битовое целое число без знака, ио не подходит для знакового числа, Однако в SQL Server есть 64-битовое целое число, которое полностью покрывает текущий стандарт IP-адресов, но требует вдвое больше памяти. bigint Целые числа от -9 223 372 036 854 775 808 до 9 228 872 036 854 775 807 (то есть от -3й до 2й - 1). Единственная причина использовать 64-битовый тип данных bigint (большее целое) — в качестве первичного ключа для таблиц, где вы будете иметь больше чем два миллиарда сярок, или если ситуация непосредственно диктует ото, наподобие ситуации с IP-адресам и, которую мы только что обсудили. smallint Целые числа от -32768 до 32767 (или от -218 до 2ls - 1). Если мы можем гарантировать, что нам нужно меньше чем 32767 элементов, может быть полезен smallint (малое целое). Он требует 2 байта хранения,
Использование smallint может выть компромиссом. С одной стороны, это экономит два - байта по сравнению с типом int, но с другой стороны smallint недостаточно велик, чтобы использовать его в любых случаях. Нели данные могут вписаться в ограничения этого домена, к ваша таблица будет очень большой, стоит использовать smallint, чтобы сэкономить даа байта. Однако использование smallint для уменьшения БД может привести к исчерпанию места» что потребует изменить весь код, чтобы использовать другой тип данных, — все это может оказаться слишком сложным. Мы будем использовать для нашей ВД тиа int. Также обычно плохо использовать smallint для первичного ключа (кроме осевых ситуаций, типа того, когда нам нужны миллиарды строк, или когда байт или два приведут к изменению в функционировании). Однородность тревует кодировать нашу ВД более последовательно. Это может показаться несущественным, но большинство средних систем намного более легко закодировать, когда вы автоматически знаете, каков тип данных. Одно из использовании smallint, которое возникает время от времени, — использование его как тин Boolean. Я предполагаю, что это возникает потому, что в Visual Basic 0 равен False, а -1 — True (фактически Visual Baaic будет обращаться с любым значением, отличным от нуля, как с True). При хранении данных таким образом не только огромная трата места (2 Вайта против потенциально 1/8 Вайта), ио и запутывайте всех других программистов SQL Server. Драйверы ODBC и OLJS DB делают этот перевод для вас, но даже если вы они не делали это, стоит потратить- время, чтобы написать метод или функцию на Visual Basic, чтобы перевести True в значение 1. tinyint Целые числа от О до 2аб. Те же самые комментарии, что и для smallint, могут быть сделяиы для tinyint (очень маленькое целое). Ът tinyint очень маленький, использует один байт для хранения, но мы редко иснользуем его, потому что имеется очень немного ситуаций, где мы можем гаранпфовать, что значение никогда не будет превышать 255 или принимать отрицательные значения. Единственное использование, которое я сделал ро отношению к tinyint за эти годы в предыдущих версиях SQL Server, — если мне нужно было группировать немного столбцов. Я не мог бы сделать это непосредственно, если бы запускал запрос подобно атому: I SELECT bitColumn, count)*) FROM testbit Group GROUP BY bitColumn В этом случае я получал ошибку. Однако я написал следующий запрос, и он заработал: SELECT CAST (bi-Column AS tinyint) AS bitCblumn, COWit(*) FROM teetbit Group I GROUP BY CAST(bitcolumn AS tinyint) SQL Server 2000 больше ие делает это различие, но оно может все еще вызвать замешательство, если вы должны писать запросы, которые нужны для перехода от одной к другой версии SQL Server.
decimal (или numeric) Все числовые данные между -10s* - 1 и 10м - 1. Тип данных decimal (десятичный) или numeric (числовой) — точный тип данных, потому что он размещается наподобие символов (кан если вы данные имели только 12 символов, включающие цифры от 0 до 9, минус и десятичную точку). Способ, которым он (тип) размещается, предотвращает от неточности, которую мы увидим немного позже в типах чисел с плавающей точкой (float) и вещественных (real) числах. Этс, однако, приводит к дополнительным затратам при получении и управлении значениями. Чтобы определить десятичное число, мы должны задать точность и масштаб: □ Точность — общее количество значащих цифр в числе. Например, число 10 имеет точность 2, а 43.00000004 — точность 10. Точность может изменяться в пределах от 1 до 38. □ Масштаб — возможное число значащих цифр, расположенных правее десятичной точки. Используя наш предыдущий пример, 10 имело вы масштаб О, а 43.00000004 потребует масштаба 8. Числовые типы данных ограничены точностью и масштабом, определяющими, насколько велики данные. Например, возьмем следующее объявление числовой переменной: Ц DECLARE etestvar decimal(3,11 Это позволит нам вводить любую числовую величину больше чем -99.94 я меньше чем 99.94. Ввод 99.949999 допустим, но ввод 99.95 — нет, потому что это будет округлено да 100.0, что не может быть отображено а помощью decimal (3, 1), Например, следующее операторы; I SELECT gtaswar « -10.155555555 SELECT gtestvar возвращают -10,2. Когда язык программирования компьютера делает это для вас, операция рассматривается как неявное преобразование. Это является и благословением, и проклятием. Следует иметь в виду, что вы должны быть очень осторожны у границы допустимых значений типа данных. Обратите внимание, что имеется параметр настройки SET NUiffiRlC_R£>MDABQRT ON (задание отбрасывания при округлении числовых данных), который вызовет ошибку, если произойдет потеря точности от неявного преобразования данных. Его весьма опасно использовать, так как приложения, использующие SQL Server, могут быть выброшены, вояж параметр включен. Однако если вы должны предотвратить неявное округление с помощью системных ограничений, это очень ценный инструмент. Хранение числовых типов зависит от того, какая требуется точность: Точность Требуемое число байт 1 + 9 5 10+19 е 20+28 13 29 + 28 17
Что касается использования, то тип данных decimal должен вообще использоваться экономно. Нет ничего плохого е типам вообще, но он требует немного большей обработки, чем целые или вещественные данные, и, следовательно, имеется помеха в выполнении. Вы должны использовать втот тип, когда имеете определенные значения, которые желательно хранить, не теряя точности. Дополнительно с темой потери точности мы более подробно будем иметь дело в разделе "Приближенные числовые данные". Денежные величины Денежные типы данных — обычно целые числа с десятичной точкой, помещенной в число. Так как это всегда имеет одну и ту же точность и масштаб, процессор может работать е такой величиной подобно обычному целому числу, а затем механизм SQL Server может вставить десятичную точку назад, как только математические функции будут выполнены. Имеются две разновидности денежных переменных: □ money (денежный) — величины от -622 387 208 вВВ 477.6808 до 922 837 303 885 477,6807 с точностью одной десятитысячной денежной единицы. Как можно было Вы ожидать, это, очевидно, целое число из 64 бит и требует восьми байт хранения. Q stnalImoney (малый денежный) — величиям от =214 748.3648 до +214 748.3647 с точностью одной десятитысячной денежной единицы. Это требует четырех байтов хранения. Типы данных money и stnallmoney — превосходное средство хранения для деиежных величин практически с любыми требованиями, если только ве требуется хранить наши денежные величины с более чем четырьмя десятичными знаками, что обычно не встречается в большинстве ситуаций. Столбцы идентификации Для типов данных int или decimal (с масштабом 0) мы можем создавать автоинкрементный столбец, чьи значения гарантированно будут уникальными для таблицы. Столбец, который реализует идентификацию, должен быть также определен как NOT WLL (не равен null). I CREME TABLE testidentity ideiitityColumn int MOT MULL ЭИИ« (1, 2) , value varchar(lO) 80“ NULL ) Здесь создается таблица testidentity (проверка идентификации), состоящая из двух столбцов: первого столбца ideatityCaluim (столбец идентификации) типа int, элементы которого не могут принимать значение NULL и использующего функцию IDENTITY (идентификация), и второго столбца value (значение) типа varehar (10), элементы которого не могут принимать значение NULL. В этом операторе CREATE TABLE я добавил функцию IDENTITY для столбца identityColumn. Дополнительные значения в круглых скобках известны как начальное значение и приращение. Начальное значение 1 указывает, что мы начнем с первого значения, равного 1, а приращение говорит, что второе значение будет 3, далее 6 и т. д.
В следующем фрагменте мы вставляем три новых строки в таблицу testldent-ity: INSERT VALUES INSERT VALUES INSERT VALUES SELECT INTO teatidentity (value) (’один' J INTO cestldentity (value) ('лее') INTO testidentity (value) (' три') * eROM testidentity Это дает следующий результат: identttyColumr» value 1 3 5 один два три Свойство идентификации фантастически удачно для создания первичного ключа типа указателя, который является и небольшим, и быстрым. (Следует помнить и другое — он не должен выть единственным ключом таблицы, или мы фактически не будем иметь никакой уникальности, за исключением этого произвольного значения!) Тип данныхint требует только четырех байтов и очень хорош для большинства таблиц, которые мы создаем, если они будут иметь меньше чем 2 миллиарда строк. Одно замечание. Идентифицирующие значения могут иметь пропуски в последовательности. Если происходит ошибка при создании новой строки, идентифицирующее значение, которое должно быть использовано, будет потеряно. Аналогично, если строка удаляется, удаленная величина не будет повторно использоваться. Следовательно, вы не должны использовать столбцы идентификации, если не можете допустить это ограничение на значения в вашей таблице. Если вы должны гарантировать уникальность по всем таблицам или даже нескольким БД, рассмотрите создание столбца с типом данных uniqueidentifier (уникальный идентификатор) и с отказом от автоматического формирования величия. Мы обсудим эту реализацию позже в разделе, посвященном типам данных. Приближенные числовые данные Приближенные числовые значения содержат десятичную точку и размещаются в формате, которым можно быстро манипулировать, но они точны только до 15-го десятичного знака, Приближенные числовые значения имеют некоторые очень важные преимущества, как мы увидим позже в этой главе. Приближенный — неприятный термин, но фактически правильный. Он означает типы данных real (вещественный) и float (с плавающей точкой), которые соответствуют стандарту ШЕЕ 75454 величин с плавающей точкой одинарной и двойной точности. Причина, почему оии называются величинами с плавающей точкой, связана со способом их хранения. В основном, число хранится как 32- или 64-битовая величина с четырьмя частями: □ знак — определяет, имеет ли оно положительное или отрицательное значение:
□ показатель — показатель мантиссы по основанию 2: О мантисса — хранит фактическое число, которое умножается на показатель; О смешение — определяет, положительным или отрицательным является показатель. Полное описание того, как эти типы данных фактически сформированы — вне возможностей этой книги, но может выть получено из содержания ШВЕ bwww,ieee.org для формального понимания. Имеются две разновидности типов данных с плавающей точкой, доступных в SQL Server: □ float [ (N) ]— величины в диапазоне от -1.79В +308 до 1.79Е +808. Тин данных float позволяет нам определять конкретное число бит, используемых в мантиссе — от 1 до 58. Мы определяем это число вит величиной N. □ real — величины в диапазоне от -3,40В +38 до 3.408 +38. real — сиионим для типа данных float (24}. Размещение и точность для типов данных float следующие: N (число бит мантиссы) Точность Размер для хранения 1 + 24 7 4 вайта. 25 + 53 15 в байт Следовательно, мы сахобни представить большинство значений or -1.79В -308 до 1.79Е +308 с точностью 15 значащих цифр. Это — не так уж и много значащих цифр по сравнению с числовыми типами данных, но, несомненно, достаточно почти для любого научного приложения. Обратите внимание, что в предыдущем абзаце мы сказали "представить большинство значений". Это — проблема, крторую мы должны понять. Имеются некоторые значения, которые просто не могут выть размещены в формате е плавающей точкой. Классический пример «ого — значение 1/10. Нет никакого способа хранить это значение точно. Когда мы выполним следующий фрагмент кода: I SELECT CASTQ AS flcat(53H f CAST (10 AS floattSSH AS funnyreBult то ожидаем, что результат будет точно равен 0.1. Однако, выполнив оператор, полущим: funny_result 0.10000000000000001 Даже если мы изменим ввод на: В SELECT CASTf.l AS float! AS -funny_re-sult мы получим тот же неправильный результат, Различие интересно, во несущественно для всех вычислений, кроме высокоточных. Типы данных float и real ~ встроенные типы данных, с которыми можно работать, используя сопроцессор для вещественных чисел. Сегодня во всех процессорах этот сопроцессор — часть главного центрального процессора, и хотя это несколько более дорого, чем математика целых чисел, которая может быть
выполнена непосредсттеиио в регистрах центрального процессора, но ивиамвримо лучше, чем типы данных точных нецелых чисел. Недостаток их в том, как организовано хранение данных с плавающей точкой. Любое дальнейшее обсуждение недостатков размещения величин с плавающей точкой — вне возможностей этой книги, Приближенные числовые типы данных очень важны для хранения измерений, когда требуются вычисления. Происходящее округление несущественно, поскольку пока очень немного устройств, которые могут производить измерения е 15 значащими цифрами, и, поскольку вы будете, вероятно, делать большое количество математических вычислений е измеренными величинами, результат будет значительно лучше по сравнению с использованием десятичного типа. При кодировании значений с плавающей точкой вообще лучше всего стараться избегать сравнивать эти значения, используя для сравнения операцию Поскольку значения приблизительны, не всегда возможно ввести точное значение, которое было размещено. Вели вам нужно искать точные значения, то лучше использовать числовые тины данных. Дата и время-дата Если вы вспомните, мы не первый раз обсуждаем типы данных datetime и smaildatetime. Первый раз мы столкнулись с ними при обсуждении нарушений 1НФ. В этом разделе мы рассмотрим характеристики типов datetime, а затем обсудим некоторые альтернативы. smaildatetime Этот тип (укороченная дата-время) представляет данные даты и времени е 1 января 1800 года до 6 июня 207® года. Тип smaildatetime имеет точность в одну минуту. Это требует четырех байтов хранения. Тип smaildatetime — лучший выбор, когда вы должны хранить дату и, возможно, время некоторого события, где точность в минуту — не проблема. Вели требуется более высокая точность, то, вероятно, лучше выбрать тип datetime (дата-время), datetime Этот тип представляет данные даты и времени с 1 января 1753 года по 81 декабря 8999 года. Размещение типов данных smaildatetime и datetime интересно. Тип datetime размещается в два 4-байтоаых целых числа. Первое целое число хранит число дней до или после 1 января 1990 года. Второе хранит число миллисекунд после полуночи -конкретного дня. Значения ежа 1.Idatetime размещаются сходным образом, но используют S-байтовые целые числа вместо 4-ваятовых. Первое — 2-байтовое целое число без знака, а второе используется, чтобы хранить числа минут после полуночи. Тип datetime иаиет точность 0.08 секунды. Использование восьми байт, однако, требует значительного объема памяти. Используйте datetime или когда вам нужен расширенный диапазон, или когда вам нужна высокая точность. Редко бывает, когда вам нужна такая точность. Единственное приложение, которое приходит на ум -— столбец временных меток (ие путайте с типом данных times tan®, который мы обсудим в одном из последующих разделов этой главы), который используется, чтобы точно обозначить время операции. Нет ничего удивительного, если мы хотим получить информацию о времени, например, время в секундах, прошедшее между двумя действиями, или если мы хотим использовать значение datetime в механизме управления параллельным выполнением операций. Мы рассмотрим глубже тему управления параллельным выполнением операций позже в этой главе,
Использование определяемых пользователем типов данных для управления датами и временами Одно сяово относительно хранения значений даты н времени. Использование типов данных datetime, когда вам нужна часть, связанная только с датой, или только со временем, — сплошные мучения. Возьмем следующий пример кода. Мы хотим найти каждую запись, где дата равна 12 июля 1967 года. Если мы закодируем его самым очевидным образом: I SELECT * FROM employee WHERE birthDate - ‘July 11, 1967* Здесь employee — служащий, birthDate — дата рождения. Мы получим совпадение для каждого служащего, где birthDate точно соответствует “Июль 12, 19в7 00:00:00.000''. Однако если дата имеет вид "Июль 12, 1967 10:05:28.800", как его могло бы выть в соответствии е проектом (то есть записано точное время рождения), или но ошибке (то есть, управление датой могло послать текущее время по умолчанию, вели мы забыли очистить это) — мы можем замучиться необходимостью писать запросы подобна - следующему, чтобы ответить на вопрос, кто был рожден 12 июля. Чтобы сделать это, мы были бы должны переписать наш запрос следующим образом: (SELECT • FROM employee WHERE birthDate >= 'July 12, 1957 0:00:00.160* AND birthDate < 'July 13, 1967 0:00:00.000* Заметьте, что мы не использовали оператор BETWEEN в »той операции. Если бы мы хотели использовать этот оператор, то получили бы: I WHERE birthDate BETWEEN: * July 12, 1967 0:00:00.00' AND 'July 12, 1967 23:59:59.997' ~ сначала исключив всякие даты 1В июля, а затем избегая любые округления типа данных datetime, Так как точность — 0.03 секунды, то когда будет оцениваться величина 'July 13, 1967 23:59:иМ0Т, она будет округлена до ’July 13, 1967 0:00:00.00'. Все это не только неприятно и тяжело, но н может быть неэффективно. В случае, где нам не нужна часть типа данных datetime, связанная е датой, или часть, связанная со временем, лучше всего подойдет создание собственного тина данных. Например, возьмем типичный случай хранения времени, когда кое-что происходит, без учета даты. Имеются две возможности разобраться с этой ситуацией. Обе — иамного лучше, чем просто использование типа данных datetime, но каждая имеет свой собственным набор проблем. □ Использование нескольких столбцов — каждый просто для часов, минут, секунд и отдельных долей секунд, если ато вам нужно. Проблема здесь состоит в том, что не просто сформировать запрос. Вам нужно, по крайней мере, трн столбца, чтобы запросить нужное время, и вам потребуется проиндексировать все столбцы, используемые в запросе, чтобы получить хорошее выполнение на больших таблицах. Размещение будет лучше, поскольку мы можем испоямовать для отдельных столбцов тип tinyint. Если вы котите посмотреть ва то, что происходило в течение десятого часа дня в течение недели, то вы могли бы использовать этот метод.
□ Использование единственного столбца, который содержит число секунд (или частей секунд) между полувочью и текущим временем. Однако в этом случае нельзя сформировать разумный запрос по веем значениям, которые возникают в течение, скажем, десятого часа, и если бы мы определили число секунд, это было бы всякое значение между 36000 и 39600. Однако это идеально, если мы используем данные в некотором виде внутренней процедуры, с которой пользователи-люди не должны взаимодействовать. При использовании этих столбцов мы могли вы создать определяемые пользователем функции, чтобы преобразовывать наши типы данных к удобочитаемым значениям. В качестве другого примера иы могли, вы создать наш собственный тин даты, который просто реализован как целое число. Мы могли вы добавить определяемый пользователем тип, названный intDateType (большее освещение этой темы будет дано позже в агой главе): iEXEC sp_addtу rt B-tyaename ” intDateType, gphystype ~ Integer, eHULLtype - 'ЖЛД.', Sowner ~ 'dbo' GO И затем формируется определяемая пользователем функция для преобразования значений переменной с нашими данными в реальную переменную типа datetime, чтобы использовать в вычислениях даты или просто, чтобы показать клиенту. I CREATE FUSCTIOH intOateType$convertToOat.etlTEe ( geateTime IntPateType RETURNS datetime AS BEGIN REMRS(date*dd(day, Sdatetinse, 'jan 1, 2201'J > END GO Из этого основного примера должно быть довольно очевидно, что определяемая пользователем функция будет чрезвычайно важной частью нашего кодирования приложевий SQL Server. Мы будем использовать ее несколько раз в згой главе, чтобы иллюстрировать то или иное положение, но мы рассмотрим ее более полно в следующей главе, где будем ее использовать для защиты наших данных. Мы увидим ее снова в главе 11, когда будем управлять данными. В данный момент просто важно обратить внимание, что эти особеетости существуют. Чтобы проверить нашу новую функцию, мы могли бы начать е просмотра второго дня года, преобразовывая дату в переменную типа datetime: Ц SELECT dtoG.intDe.teTyp»3conv»rtToDateti».e (1) AS convertwWalue что возвращает: cofwertedValue 2001-01-02 00:00:00.000
Мы должны были бы построить явеиолыю других функций ввода данных, чтобы взять переменную типа datetime и разместить ее как новый тиа данных. Хотя мы преднамеренно не.рассматривали многие детали определяемых пользователем функций, я предложил -дианазон возможностей решения проблемы даты или времени. Тип данных datetime представляет проблемы для программистов, с которыми вы должны иметь дело. Только будьте осторожны. Двоичные данные йжт-пи: данные поаволяют нам размещать последовательность вит е диапазоном от одного байта (восемь вит) до примерно двух гигавайт вит (величина il - 1 или 2 147 483 в47 байт). Концепция, которую мы начнем рассматривать связи с двоичными типами данных — использование данных переменной длины. В основном это означает, что размер хранилища данных для каждого экземпляра столбца или переменной этого типа данных может изменяться в зависимости от того, сколько данных размещается. Во всех предыдущих типах данных хранилище было фиксировано, потому что размещались представления величин фиксированной длины, которые интерпретировались, используя некоторую формулу (например, целое число в 32 вита или дата —• число единиц времени, начиная е некоторого момента времени). Любой тип данных, который допускает- значения переменной длины, обычно связан с вопросом размещения значений вместе, подобно буквам в слове. Данные переменной ддины обычно включают две части — значение, определяющее, какова длина данных, и данные сами по себе. Имеется некоторый верхний предел для размещения данных переменной длины, но это обычно лучше, чем размещение данных в столбцах фиксированной длины со значительным потраченным впустую местом. Меньшие данные означают большее количество записей на .страницу, что уменьшает время ввода/вывода, а операции ввода/нывода гораздо более дорогостоящи, чем расположение полей в записи. Двоичные данные размещаются в трех различных вариантах: □ binary (двоичный) — данные фиксированной длины с максимальной длиной S000 байт. Используйте только тип binary, если вы знаете, насколько велики будут ваши данные. Хранилище будет яквивалентно числу байт, которое мы объявим для переменной. □ varbinary (переменный двоичный) — дм двоичных данных с переменной длиной и максимальной длиной 8000 байт. □ image (изображение) — для действительно больших данных изображений переменной длины е максимальной длиной 2s1- 1 (2 147 483 647) байт. Одно из ограничений двоичных типов данных — то, что они не поддерживают побитовые операторы, которые позволили бы нам создавать конкретное очень мощное хранилище битовой маски, позволяющее сравнивать два двоичных столбца, чтобы видать не только то, что они отличаются, но и как они отличаются. Общая идея двоичных типов данных состоит в том, что они хранят последователыюсти бит. Побитовые операторы могут работать с целыми числами, которые физически размещаются как биты. Причина этой несогласованности фактически довольно ясна с точки зревия виутревиего процессора запросов. Побитовые действия — фактически действия, которые выполняются в процессоре, в то время как двоичные типы данных — специфические особенности SQL Server.
Дополнятеданая кониеиция. которую мы должны обсудить — то, как SQL Server хранит большие объемы данных, таких как тип спаде, обычно называемый как BLOB (-Binary Large Objects) — большие двоичные объеь;ты. Когда тип данных должен хранить данные, которые яаляю гся потен циально больше по размеру, чем страница SQL Server, требуется специальная обработка. Вместо хранения фактических дааных на странице с записью, размещается лишь указатель на другое физическое место памяти. Указатель — 16-байтовая двоичная величина (тот же самый рассмотренный нами тип даиных), которая укавывает на другую страницу в ВД. Страница может хранить значение одного штнескольких BLOB, и BLOB может ааиимвть много страниц. Использование полей binary» особенно полей image, довольно ограничено. В основном они могут применяться для хранения любых двоичных значений, с которыми SQL Server не работает. Мы можем хранить текст, изображения в формате JPEG и GIF (JPEG — стандартный алгоритм сжатая изображений, GIF — формат графического обмена) и даже документы Word и электронные таблицы Excel. Так почему использование ограничено? Простой ответ — функционирование. Вообгце-то плохо использовать тии даиных image, чтобы хранить очень Волыние файлы, потону что их долго восстанавливать из ВД. Когда нужно хранить данные изображения в БД, мы будем обычно хранить имя файла совместного доступа. Программа доступа будет просто использовать имя файла, чтобы обратиться к внешнему хранилищу файла. Файловые системы создаются именно для хранения и обслуживания файлов, так что мы используем их по прямому назначению. Строки символов Большинство данных, размещаемых в SQL Sarver, использует символьные тины данных. Действительно, обычно достаточно большое количество даиных размещается в символьных типах данных. Часто символьные поля используются, чтобы хранить неси мюльные данные типа чисел и дат. Хотя это фактически и допустимо, но это — не идеал. Для систем инициализации, чтобы хранить число с восемью цифрами в символьной строке, требуется, по крайней мере, восемь байт, в то время как целое число требует четырех байтов. Поиск на целых числах гораздо легче. потому что 1 всегда предшествует 2, в то время как в символьной строке 11 предшествует 2. Кроме того, целые числа размещаются в формате, которым можно управлять, используя внутреняяе функции процессора в противоположность специальным функциям SQL Server для работы е данными. char Тип данных char (символьный) используется- для символьных данных фиксированной длины. Вы должны выбрать размер данных, которые желаете хранить. Каждое значение будет размещено в одно и то же число символов, максимум до 8060 Вайт. Хранилище — тачное число Вайт в соответствии с определением столбца независимо от фактически размещенных данных; любое остающееся место справа от последнего символа заполняется пробелом. Обраwume «шжокяв что в версиях SQL -Server, ранее чем 7.0, реализация столбцов char отличается, Установка ключа ANST_PADPING точно определяет, как это фактически происходит. Если этот ключ установлен, таблица точно такая, как мы описали; если нет., данные будут. размещаться так, как мы рассмотрим в разделе ниже, посвященном типу vaxchaxr Обычна лучше задавать установку набора значений ANSI для большей стандартизации (или даже большего ограничения), поскольку зти установки ' помогут вам избежать проблем с модернизацией SQL Server в вашем кодировании, которые имеются при переходе от версии к версии.
Максимальный предел для типа char — 8060 байтов, ио очень маловероятно, что вы когда-либо доберетесь до мага предела, Для символьных данных, где ожидается большее число символов, вы использовали бы один ив других сшивальных типов. Тил данных char следует использовать только в случаях, где гарантируется одно и то же число символов в каждой строке, и каждая строка не содержит значение ЯСЫ» По этой причине мы используем мот тип очень редко, так как для бальпшиства данных не атфадедлив» соглашение о равной- длине. Имеется один пример, где тип char мог вы использоваться довольно часто — это идентиф1п<ационные числа. Они могут содержать смесь алфавитных и числовых символов типа номеров транспортных средств (VIN — Vehicle Identification Number). Обратите внимание, wo ато — фактически составной атрибут, поскольку вы можете определить многое относительно автомобиля по era номеру. Другой пример, где поле char обычно используется — номера социального обеспечения. галйбиг Для типа данных varchar (переменная строка символов) вы выбираете максимальную длину данных, которые желаете разместить, — до 8000 байтов. Тип данных varchar гораздо более полезен, чем char, поскольку данные ив должны иметь одну и ту же длину, и SQL Server не дополняет имяши памяти пробелами. Одааио имеются некоторые дополнительные издержки хранении данных переменной длины. Используйте тип данных varchar, когда ваши данные обычно небольшие, скажем, несколько сотен байт или меньше. Положительный момент относительно столбцов varchar состоит в том, что независимо от того, насколько большим вы задаете максимум, размещенные данные — это текст в столбце пли» немного дополнительных байт, которые определяют длину данных. Мы будем вообще стараться выбирать максимальный предел для нашего типа данных, который является подходящим значением, достаточно большим, чтобы обеспечить большинство ситуаций, но не слитком большим, чтобы быть непрактичным для наших приложений и отчетов. Например, возьмем имена людей. Они, очевидно, требуют типа varchar, но какой длины мы должны позволить быть датаым? Имена обычно имеют максимум пятнадцать символов, хотя вы могли бы определить двадцать или тридцать символов для маловероятных исключений. Данные varchar — наиболее распространенный тип размещение для неключевых значений, который мы будем использовать. text Тип данных text (текст) используется, чтобы размещать большое количество символьных данных. Он может хранить максимум 2 147 483 647 (2®1 - 1) символов, или 2 Гб. Размещение типа text во многом подобно типу данных image. Однако дополнительная особенность позволяет нам обходиться без указателя и размещать данные text непосредственно на странице. Это возможно в SQL Server, если выполняются следующие условия: □ величина столбца меньше, чем 7000 Вайт; □ данные во всей записи не будут превышать одной страницы данных; □ опция таблицы Text In Row установлена, используя процедуру sp_tableop- ion, примерно следующим образом: Ц EXECUTE sp_tableoption 'TableName' , 'text in row', '1000'
В этом случае любые столбцы текста в таблице TableName будут хранить свой текст со строкой вместо выделения их в даугие страницы, если только столбец не достигает 1000 символов или полнау строка не слишком велика, чтобы уместиться на странице. Если строка становится слишком большой, чтобы уместиться на странице, одаи или большее количество столбцов текста будет удалено из строки и помещено на отдельных страницах. Эта установка позволяет нам использовать значения текста разумным образом, так, что когда размещаемое значение маленькое, функционирование в основном то же самое, что и дам столбца varchar. Следовательно, мы можем теперь использовать тип данных text, чтмы иметь символьные данные, где длина может изменяться от десяти символов до 50 Кб текста. Это очень важная особенность для столбцов типа примечаний, когда пользователи хотят иметь место для неограниченных текстовых примечаний, но часто помещают только десять или двадцать символов в каждой строке. Другая проблеме с полями text — то, что они не могут быть индексированы, используя обычные методы индексации. Как рассмотрено в предыдущей главе, если вы должны выполнять сложный поиск в текстовых столбцах, то можете использовать возможности полнотекстового поиска в SQL Server 2000, Следует также отметить, что даже если бы мы могли иидексировать текстовые столбцы, это вряд ли выла хорошая идея. Так как столбцы типа text или даже большие столбцы типа varchar могут выть настолько большие, то невозможно их использовать для индексов, имеющих предел в 900 Лит. Строки символов Unicode До сих пор типы символьных данных, которые ми обсудили, касались хравевии обычных АБСП-данных. В SQL Server 7.0 (и NT 4.0) фирма Microsoft реализовала новый стандартный формат символов, называемый Unicode. Он определяет 16-битоаый формат стшволов, который может хранить символы ие только латинского алфавита. > AS СП-системе символов из семи бит (с восемью битами для латинских .расширений) мы были ограничены 256 различными символами, которых выло достаточно для большинства англоговорящих людей, но недостаточно для других языков. Восточные языки имеют символ для каждого отдельного слога и эти символы не являются алфавитными; языки Среднего Востока используют несколько различных символов для одной и той же буквы в зависимости от ее положения в слове. По агой причине был создан 16-ввтовый стандарт символов, позволяющий нам иметь 655В6 различных символов. Для этих данных мы имеем тшш данных r.char, nvarchar и ntext. Они — точно те же самые, что и типы, имеющие подобные названия (без а), которые мы уже описали, если бы не одна вещь: использование Urdcode удваивает число байт для хранения информации, так что требуется вдвое больше места; это уменьшает наполовину число символов, которые могут быть размещены. Один небольшой совет. Если вы хотите определить значение Unicode в строке, вы добавляете символ N к началу строки, наподобие следующею: В SELECT N'Unicode Value' Вариантные данные Вариантный тип данных позволяет вам хранить почти любой тип данных, который был рассмотрен. Эго позволяет нам создавать столбец или переменную, где мы не знаем точно, какие данные будут размещены. Это новая особенность SQL Server 2000, Название нового типа данных — sql_variant (вариантный тип для SQL), и он позволяет нам хранить значения любого поддерживаемого SQL Server типа данных, кроме text, ntext, timestamp и sql_variant (может показаться странным, что вы не можете хранить вариантный тип в " вариантном типе, но все это говорит о том, что тип данных sql_variant фактически не существует, a SQL Server выбирает лучший тип хранения для размещения вашего значения).
Тип данных s q l_va г tan t позволит нам создавать определяемые пользователем таблицы типа ’набора свойств'*, которые помогут нам избежать очень длинных таблиц со многими столбцами, которые могут быть, а могут и не быть заполнены, Возьмем таблицу entityProperty (свойство сущности) в следующем примере: entityProperty : entityproperty;с int__________ , entitylrli int : ргареЛуУМиа!: lot property Value 2: datetime I propertyyalLie3i varbineryio) —Я prapertyValue4; varctw(60J propertyVeiueS; varchar(2000) propertjVaiueS varchar(30) propertjValue7: nchan20) P^pertyVaiueN' varcnan 30; } | Здесь entity — таблица "сущность": entity Id — идентификатор сущности; Attributed— N-й атрибут; entityproperty — таблица "свойство сущности" ; entityPrcpertyld — идентификатор свойства сущности; propwtyValued — N-e значение свойства. В этом примере мы имеем N столбцов е N типами данных, которые могут принимать значения HULL, что позволяет пользователю хранить любое из этих (или несколько) значений. Этот тип таблицы обычно называется разреженной таблицей. Проблема с этим примером состоит в том, что, если пользователь придумывает новое свойство, мы будем вынуждены изменить таблицу, пользовательский интерфейс и любые программы, которые связаны со столбцами таблицы. С другой стороны, если мы реализуем таблицу следующим образом: i entity j mtityld: int ; j AtHitJUtel: v a r chari 2 0 i i AttnoutcZ: varchar(20) AttrtbuteN: varchar(20) entigProperty entityPrope'tyid int_______ entity-propertyГуреИ: int ' entity Id. int oroperiyVaiue: sqLvariam entityPropertyType entiiypropertyTypeld, int name; rarchar(20) Здесь дополнительно entityPropertyType — таблица ‘тип. свойства сущности"; entityPropertylypeld — идентификатор типа свойства сущности; пале — название типа свойства сущности.
... то каждое из свойств, которое мы реализовали в предыдущем примере, добавляя столбец в таблицу entityproperty, будет теперь добавлено как экземпляр в таблице encityPropertyType, и значение может быть размещено в столбце propertyvalue. Какой бы тип данных ни понадобился для свойства, он будет размещен как aql_variant. Таблица ontityPropertyType могла бы быть расширена для включения многих других свойств, при атом пользователю ие нужно производить серьезные изменения в ВД. И если мы реализуем нате решение формирования отчетов таким способом, чтобы ня тли новые отчеты знали относительно любых изменений, нас не будут смущать никакие новые свойства. Недостатки вариантного типа Если данные размицеиы в столбце типа eql_variant, ими нелегко управлять. Я оставлю это читателю, который может полностью прочитать информацию в SQI, Server Books Online относительно вариантных данных. Проблемы возникают в следующих случаях: □ Назначение данных из столбца типа sql_variant в более строгий тип данных — мы должны быть очень осторожны, поскольку правила перевода переменной из одного типа данных в другой сложны и могут вызвать ошибки, если данные ие могут выть преобразованы. Например, величина типа varchar(W) 'Не дата’’ ие может быть преобразована к типу данных datetime. Такие проблемы действительно станут актуальны, когда вы начинаете восстанавливать вариантные данные иа типа данных sql_variant и пытаться управлять ими. □ Считается, что-величины HOLL типа sql_variant не имеют никакого типа данных — следовательно, вы должны будете обращаться со значениями MULL в sql_variant по-разяому для разных типов данных. □ Сравнения вариантных величин о другими типами даиных могут вызвать ошибки программирования, потому что если только одна из величии имеет тип sql_variant, компилятор будет знать, что вы запускаете оператор с двумя несравнимыми типами данных, например, SintVar - tvarcharVar. Одаако ее» эти две переменные были определены как sql_varlent, и типы данных не соответствуют, то значения яе будут соответствовать из-за несовместимости типов данных. Имеется функция, которая определяет тип значения, находящегося а вариантном столбце: I DECLARE gvarcharVariant sql_variant SET gvarcharVariant - '1234567390' SELECT gvarcharVariant AS varcharVariant, SQL_VARIAST_₽K>FSRTY(evarcharVariant, 'BaseType') as baseType, SQL_VARIAKT_₽RQFERTy (SvarcharVariant, 'MaxLength'j as MxLeneth Здесь определяется переменная va rcharVaгj.tnt вариантною типа, в которую помещается строка '1234567890', после чет определяется содержимое переменной varcharVariant, его вазовый тиа (baseType) и максимальный размер (saxLength}, используя фрикцию SQL_VARIANT_PRQPERTr. Этот запрос возвращает: varcharVariant baseType maxLength 1234567890 varchar 10
Другие типы данных Следующие тины данных несколько мевее часты, но они все же очень полезны timestamp (адм rowversion) Тип данных timestamp (время совдания/обновдания) или rowversion (версия строки) — уникальное число в рамках ВД, Когда вы имеете столбец типа time sr amp. значения его изменяются с каждой корректировкой каждой строки. Гарантируется, что значения в столбцах типа timestamp будут уникальными по всем таблицам. Он имеет довольно странное название, поскольку не имеет никакого отношения ко времени, это просто уникальное значение, чтобы сообщить вам, что ваша строка изменилась. Столбец таблицы типа timestamp (вы можете иметь только одни столбец) обычно используется как данные для механизма "опти?«истическсй блокировки". Мы обсудим это позже в данной главе, когда будем рассматривать служебные столбцы/ Тип данных timestamp ~ сомнительное благо. Он размещается в восьмибайтовой величине типа varbinary. С двоичными вначеяиями не всегда легко иметь дело, и они зависят от того, какой механизм вы используете для доступа к вашим данным. При использовании ODBC или OLE DB и АПО они могут, быть на первых порах коварными. Это связано с тем, что они являются двоичными значениями, и для восстановления их вы должны использовать механизмы формирования фрагментов BLOB-данных. SET WOCOUNT ОН CREATE TABLE testTimestMip ( value varchartZO) KOT HOLL auto_rv timestamp HOT HOLL > INSERT INTO testTimestamp (value) values ('Ввод') SELECT value, auto_rv FRCB testTimestamp UPDATE testTimestamp SET value - 'Первое изменение* SELECT value, auto_rv from testTimestamp UPDATE testTimestamp SET value - 'Последнее изменение' SELECT value, auw rv FROM testTimestamp Здесь создается таблица testTimestamp (проверка timestamp), содержащая два столбца: value (значение) типа varchar (20) и auco_zv (автоматическая фиксация изменения строки) типа timestamp. Далее в столбец value помещаются поочередно значения ’Ввод', 'Первое изменение' и 'Последнее изменение’, а в промежутках между вводом данных проверяется содержимое столбцов value и, autq^rv.
Возвращаются следующие значения: value auto_rv Ввод 0x0000000000000089 Value auto_rv Первое изменение ОхООООООООООООООвА Value atrtojv Последнее изменение ОхООООООООООООООвВ Мы не трогали переменную auto_rv, и все же она дважды увеличилась. Однако вы не можете рассчитывать, что значение переменной THnatimestarap будет последовательным., поскольку корректировки других таблиц будут изменять его значение. Лучше также не считать, что величина является числом изменений, выполненных вашим иодом. То, как конкретно реализован тип timestamp, — деталь, которая может выть изменена в будущем (из SQL Server 2000 Books Online: "Определение timestamp в будущих версиях SQL Server будет изменено, чтобы совместить с определением timestamp в SQL-99."!, Если будет найден лучший метод задания уннкальяов величины в пределах ВД, который окажется хоть на волос быстрее, он будет, вероятно, использоваться. Вы можете создавать переменные типа twestaap для получения значения timestamp, и можете восстановить последнее используемое значение time stamp черев глобальную переменную figdbta. Тема "оптимистической блокировки" будет рассмотрена более детально в главе 12, uniqueidentifier Глобальный уникальный идентификатор (GUID — Globally Unique IDen tifier) быстро становится оплотом вычислений Microsoft. Название говорит, что все ови глобально уникальны. Учитывая то, как глобальные уникальные идентификаторы формируются, имеется чрезвычайно малый шане, что когда-либо будет дублирование их значений. Ови создаются с помощью формулы, которая включает использование идентификационного номера сетевой карты (если он существует), текущей даты и времени, уникального числа таймера центрального процессора и некоторого дополнительного “магического числа". В SQL Server 2000 uniqueidentifier (уникальный идентификатор) имеет очень важную цель. Когда вам нужно иметь уникальный ключ, который гарантирует, что будет уникальным во всех ВД и сервере, то, например: I DECIARE BjuidVar uniqueidentifier SET tguidVar » new() SELECT gguidVar as gurdVar Здесь задается переменная @guidVar типа uniqueidentifier, которой присваивается значение, а патам то значение проверяется.
... возвращает значение: guidVar 6С7119D5-D48F-475C-8B60-50D0C41B6EBF Они фактически хранятся как шестнадцатибайтовые двоичные значения. Обрати» внимание, что это не точно непосредственна шестна дцатибайтовое двоичное значение. Вы не можете помещать просто любое двоичное значение в столбец qucictenti г iur, поскольку значение должно удовлетворять критериям генерации, которые не задокументированы по очевидным причинам. Вели вы должны создать автогенерирующий столбец типа uniqueidentifier, имеется свойство, которое вы можете устанавливать в операторе создания таблицы (или изменить таблицу подобным же образом). Это — свойство rowguldcol (заполнение ©голбца глобальных вдентифииаторав.для строк) и оно используется так: I CREATE TABLE guidPrimaryKey guidPrimaryKeyld uniqueidentifier NOT NULL rowguidcnl DEFAULT newTdf), value varchar(lS) ) Здесь создается таблица guidPrimaxyKey (первичный ключ в виде глобального уникального идентификатора}, в которой формируются столбцы: guldPrimazyKeyld (идентификатор первичного ключа в виде глобального уникального идентификатора} типа unlqueidentlfier и value (значение) типа varchar (10), Мы ввели здесь пару новых моментов — свойство rowguideal и значения но умолчанию, которые мы рассмотрим должным образом в следующей главе. Пока достаточно сказать, что если вы не формируете значение столбца в операции INSERT, операция по умолчаниюЕ еделам это. В напгем случае мы используем функцию newld (), чтобы получить новый uniqueidentifier. Так что, когда мы выполняем следующий оператор INSERT: INSERT INTO guidPrimaryKey(value) | VALUES ('Test') Здесь вводится только значение Test в столбец valve. ... а затем выполняем следующий оператор, чтобы посмотреть введенные данные: I SELECT ' FRCK guidFrimaryKey ... то получим guidPnmaryKeyld value 8A57C6CD-7407-47C5-AC2F-E6A884C7BB46 Test Свойство столбца rowquidcol, сформированное с пометкой uniqueidentifier, уведомляет систему, что оно аналогично значению столбца идентификаторов для таблицы —
уникальны! указатель на строку таблицы. Обратите внимание, что ни идентификатор, ни свойство rowguidcol не гарантируют уникальность. Чтобы обеспечить такую гарантию, мы должны реализовать их использование е уникальными ограничениями, которые рассмотрим позже в этой главе. Казалось бы, что uniqueidentifier будет лучшим способом реализации первичных ключей, так как, когда такие величины создаются, они уникальны по веем ВД, серверам и платформам. Однако имеются' две основные причины, почему мы не будем жспольвовать столбец uniqueidentifier, чтобы реализовать все наши аервячжые ключи: □ Требовании размещения — поскольку они размером в шестнадцать байт, то значительно больше по объему, чем обычное поле целого числа. □ Возможность ввода — так как текстовая версия глобального уникального идентификатора содержит Зв символов, очень трудно поместить его значение в запрос, и нелегко его ввести. cursor cursor (курсор) — механизм, который позволяет выполнять действия над конкретной строкой вместо обычного способа работы с наборам строк. Тип данных cursor используется, чтобы хранить ссылку на курсор SQL Server в языке Т-SQL. Вы не можете использовать тип данных cursor как тип столбца в таблице. Их единственное использование —• в коде T-SQL, чтобы хранить ссылку на курсор. Большее количество информации относительно курсоров см. в ” Professional SQL Server 2000 Programming” Роба Виейра (Bob Vieira) (Wrox Press, ISBN 1-861004-48-6). table Тип данных table (таблица) несколько похож на тип данных cursor, но содержит ссылку на результирующий набор. Название типа данных на самом деле довольно неудачное, поскольку оно вынуждает функционального программиста думать, что он может хранить указатель на таблицу. На самом деле он используется, чтобы хранить набор результатов как временную таблицу. Фактически table подобен временной таблице при реализации, Следующий пример показывает синтаксис, необходимый для использования типа переменной table. DECLARE StableVar TABLE ( id int IDENTITY, value varchar(100} ) INSERT INTO StableVaz (value) values ('Это еввяий тест') SELECT id, value ЖМ ttablev*® Здесь создается объект типа table с именем gtableVar (переменная-таблица) и столбцами id (идентификатор) типа int — автоинкрементный- и value (значение) типа varchar (100). Затем в столбец value помещается значение 'Это свежий тест. Далее анализируется полученный в столбцах id и value результат.
Запрос возвращает следующее id vaiue 1 Это свежий тест Как и с типом данных cursor, вы не можете использовать тип данных table как тип столбца в таблице, и он может только использоваться в коде Т-SQL для указания на результирующий набор. Вго основная цель — получить таблицу из определяемой пользователем функции, как в следующем примере: CREATE FUNCTION tableStMtFunction t greturnValue varchar(XOO) » RETURNS BtableVar table ( value varcnar(lOO) ) AS BEfilN INSERT INTO etableVar (value) values (greturnValue) RETURN END Здесь создается функция tableStestFunction (функция тестирования таблицы), которая возвращает величину типа varchar (100). Затем формируется объект gtableVar (переменная-таблица,) типа table со столбцам value тоже типа varchar (100). В зтат столбец помещается значение, сформированное функцией tableftestFunction. После создания функции и таблицы можно использовать следующий синтаксис: I SELECT ’ FROM dbo.tableStest-Jimction(,testVaiue' I ... который вернет следующее: value testvalue Типы данных, определяемые пользователем Мы коснулись этого ранее, когда обсуждали, как управлять типом данных datetime. Основное назначение определяемых пользователем типов — позволить архитектору создать псевдонимы для часто используемых типов данных. В объявлении типа, определяемого пользователем, мы можем задать: □ имя тина данных; □ систему типа данных, включая информацию об их длине; □ позволяет ли тип данных использовать значение NULL.
Чтобы создавать определяемый пользователем тип для нашего общего серийного номера, мы могли бы сделать следующее: IEXSC sp_addty₽e Stypename - seri&lNweber, ephystype « varchar(12), SNULLtype - ' not SUU,’ , Bownex - 'dbo' GO Здесь формируется новый тип данных с именем seriallfuntber (серийный номер), структурой, совпадающей с типом varcftar (12), не допускающий использования значения NULL, владельцем которого является пользователь с именем dbo (см. выше). Когда этот тип будет применяться, мы можем изменить возможность использования значения NULL, определив это, но мы не можем изменить сам твп данных» Мы можем использовать этот тип подавно тому, кек мы попользуем все типы данных SQL Server, как способ обеспечить детали реализации использования одного и того же тина. Мы можем объединить правила (чтобы ограничить значения, которые можно вводить при любом исиользояаиииI и имчетия по умолчанию (чтобы использовать значение не умолчанию, если конкретное значение не задано) для типа данных, Определяемые пользователем типы данных, поскольку они реализоваиы в SQL Server, имеют некоторые недостатки, которые делают их несколько неудобными для использования: □ сообщения об ошибках использования правил; □ непоследовательное назначение переменных; □ тины данных столбцов, определяемые пользователем, не будут изменяться, если изменится определяемый пользователем тип. Сейчас мы рассмотрим эти проблемы. Сообщения об ошибках использования правил Можно создать определяемый пользователем тип данных, включающий правила и формирование ошибок. Создайте определяемый пользователем тип, создайте правило для него и свяжите это со столбцом. Теперь, когда создается таблица со столбцом. имеющим новый тип данных, правило будет использовано, как и ожидалось. Однако сообщения об ошибках, которые возникнут, ничего не стоят! Например, создадим очень полезный тип данных, который назовемsocialSecurityNumber, добавим правило и свяжем это со столбцом. — создадим тип, кс-с-ый назовем SSN типа varchartll), имеяиий размер - - номера социального обеспечения, включая символы тире EXEC sp_addtypd в-typename = 'SSN', tphystype - 'varchar(111 *, gNULLtype - 'not NULL' GO — совдалда правило, которое назовем SSNRule, со слеяувввм шаблоном CREATE RULE SSfflluLe AS Rvalue LIKE '[0-9:[0-9;[0-9]-[O-9J[0-9I-[C-9j[0-9][0-9j[0-Э]' GO Продолжение кода на следующей странице
— объединение правила с образцом типа ВНЕС sp_bindrule 'SSMRule', 'SSN' GO — создание таблицы для проверки с ноаьм типом данных CREATE TABLE testRule ( id int IDENTITY, socialSecurityNueber SSN 1 GO 3<?есь выполняются операции в соответствии с комментариями. Создаваемая таблица для проверки testRule (проверка правила) содержит, два столбца: столбец id (идентификатор) автоинкрементного целого типа и saclalSecuri tyUumbet (номер социального обеспечения) типа SSN, созданного пользователем. Это спадает нам таблицу со етолвцом, который должен предотвратить ввод неправильного номера сощильиого обеспечения. Тем, в следующем фрагменте мы попробуем вставить данные в таблицу: -- внод значений в тайдииу INSERT INTO testRule (seelalSecurityNumfaer) VALUES ('438-44-3343'1 INSERT INTO testRule IsocialSecuri£y№18tb*r) VALUES ('43B-43-J343'1 — заметьте, что В вместо 8 вызовет ошибку GO Результат будет следующий: Server: Msg 513, Level IS, Stele 1, Line 1 A column insert or update conflicts with a rule imposed by a previous CREATE RULE statement The statement was terminated. The conflict occured in database-'tempdb’, table ‘ШавЫе’, column socialSecurityNumber. The statement has been terminated. (Сервер: сообщение 613, уровень IS, состояние 1, строка 1 Добавление или обновление столбца конфликтует с правилом, наложенным предыдущим оператором CREATE RULE. Оператор удален. Конфликт произошел в БД 'tempdb', таблица 'testRule, столбец 'SocialSecurityHumber. Оператор удален.) Отсюда вы можете видеть, что сообщемае об ошибке — бесполезно, поскольку оно даже не сообщает нам, какое правило выло иарушено. Обратите внимание, что при нашем тестировании мы включаем опцию NOCOLW (не вычислять), отключающую все соединения. Это позволит избежать сообщений, которые говорят нам, сколько строк изменено операцией. В данном случае зта также сделано для более наглядного примера. Мы можем сделать ото в коде, задав SET NOCOUNT ON (включить NOCOUinj.
Противоречивая работа с переменными Снкч&ла, мы рассмотрим обработку значений NULL. Когда аы в разделе Сообщения об ошибках использования правил создавали тип в коде, то установили для типа значение NOT NULL. Теперь мы выполним следующее: I DECLARE ISSNvar SSN SET>MSWar - NOLL SELECT iSSNVaat Здесь формируется переменная ^SStfVar (переменная для номера социального обеспечения) типа SSN. Затем в нее помещается значение ЖИЛ и анализируется результат. Этот код показывает адиу из непоследовательностей определяемых иадьаователем типов данных. При выполнении оператора мы могли бы ожидать, что произойдет ошибка, так как тип ее допускает значение NULL. Однако мы налучии следующее: SSNVar NULL Это, конечно, несущественно, но я предпочел бы, чтобы определяемый пользователем тип данных не позволял вводить значение NULL. .. - Другая проблема, связанная е определяемыми пользователем типами данных, возникает, когда созданная перемениая этого тип* не удовлетворяет правилу, которое ввязано с этим типом. Тем не менее, возможно задать значения переменной, определенной в использованием заданного пользователем типа данных, причем эти значения не удовлетворяют добавленному правилу, но они не могут быть помещены « йаябец, определенный тем же самым типом. Здесь мы покажем этот эффект; I DECLARE gSSWat SSN SET gSSMVar - 'Bill' SELECT fSSNVar AS SSNVar INSERT INTO testRule (aocialSecurityNumber) VALUES (gSSKVar! Здесь задается переменная SSSVar типа SSN, и ей присваивается значение 'ВНР, после чего анализируется полученный результат. Далее введенный результат помещается в столбец sociolSearrityNumber таблицы testRule (обозначения те же, что и в предыдущих примерах). Этот код приводит к следующей ошибке: SSNVar Bill Server: Msg 513, Level 16, State 1, Line 5 A column insert or update conflicts with a rule imposed by a previous CREATE RULE strtemeat. The statement was termmated. The conflict occured in database 'ternpdb!, table 'testRulen column ’sociaiSecurityNurnber'. The statement has been terminated.
(Сервер; сообщение 513, уровень 16, состояние 1, строка 5 Добавление или обновление столбца конфликтует с правилам, наложенным предыдущим операторам CREATE RULE. Оператор удален. Конфликт произошел в БД 'tempdb', таблица ’testRule, столбец 'SoclalSecurityNumber. Оператор удален.} В следующей главе мы рассмотрю! более подробно защиту наших данных от недопустимых значений, а в данный момент мы только должны понять, почему не используем средств наподобие полезного инструмента инкапсуляции. Обратите внимание, что правила не являются стандартным способом обеспечения таких защит — таким являются проверки ограничений. Однако для определяемого пользователем типа, чтобы быть полезными рвя нас, они должны быть объектом сами по севе. Для любого, кто читает эту книгу, и кто действительно любят использовать определяемые пользователем типы, я думаю, было бы важно указать некоторые из наиболее важных проблем, связанных с ними. Правила в SQL Server 2000 выглядят как особенность обратной совместимости, и как таковые не могут выть обойдены в следующей версии, по причине требований покупателей сохранить некоторые особенности. Мы Судам использовать проверки ограничений, поскольку они связаны непосредственно со столбцом, а не типом данных. Типы, определяемые пользователем, не могут быть изменены, если они используются Типы данных столбцов, которые заданы с помощью определяемых пользователем типов, не вудат изменяться, если определяемый пользователем тип изменяется. Это — самая неприятная проблема. Предположим, что мы должны изменить определяемый пользователем тип данных, скажем с char (1) на varchar(10} по какой-либо причине. Нет никакого средства "изменить" определяемый пользователем тип, и мы не можем сделать это, если он уже используется. Следовательно, мы должны изменить тип данных в каждом месте, где он используется, на базовый тип, изменить сам тип и повторно изменять тип. Не слишком приятно. По атим причинам мы не будем использовать в нашем учебном примере определяемые пользователем типы данных. Необязательные данные В главе 12 мы потратим много временя, обсуждая причину проблем при использовании значений NULL, когда они используются при поиске данных. Однако мы должны посмотреть, почему нам приходится использовать столбцы, допускающие значения NULL. Одной из целей нормализации должно быть удаление стольких значений NULL, сколько возможно. Прежде, чем мы рассмотрим, как реализовать значения NULL, нам нужно кратко обсудить, что означает "NULL". В то время как можно сказать, что ЖИЛ означает необязательные данные, это — неполное определенна, Значение NULL, должно читаться как ’неизвестное значение". В главе 12 мы представим таблицы истинности для сравнений, но пока будем говорить, что NULL означает параметр, который может быть заполнен в будущем. Для столбца создается предложение: И <имя_стелбца> <тип_данных> <=пеци5икави!1_!:1Т1> ... где мы просто изменяем <спецификация_киЬЬ> на NULL, если допускается использование значения NULL, или NOT NULL, если значения NULL не допускаются. Например,
I CREATE TABLE NULLTest NULLColumn varchar (10) Wli, notNULLColuwn varchar(l0) NOT NULL Здесь создается таблица NULLTest (тестирование значения NULL), состоящая из двух столбцов: KJLL-Colwaa (столбец, допускающий KILL) и notKJLLCalwan (столбец, не допускающий значения NULL), оба типа varchar fl Oh Здесь нет ничего удивительного. Если мы опустим спецификадаюNULL вообще, используется вариант по умолчанию для SQL Server. Чтобы определить, каково текущее свойство для ВД по умолчанию, мы выполняем следующий оператор: Q EXECUTE sp_dboption gdbname = ' tempdb', (ioptname - 'MSI Null default' Здесь выполняется стандартная процедура ap_dboption (определение опции БД) для БД с именем tempdb (временная БД), проверяющая состояние опции е именем №151 NULL default (NULL по умолчанию). Этот оператор дает OptionName (название опции} Currentsetting (текущая установка) ANSI NULL default off Чтобы задать значение по умолчанию для ВД, вы можете использовать процедуру - sp_dboption. (Я рекомендовал бы, чтобы эта установка всегда отключалась, если вы забываете устанавливать это явно; в этом случае вы не увязнете в столбцах, допускающих значение NULL и которые быстро заполнятся такими значениями, которые вы будете должны чистить.) Чтобы установить зяачение по умолчанию для сеанса работы, используйте следующую команду: Ц SET AMSI_NOLI,_DFLT_ON OFF — или ON, если по умолчанию используется HULL Пример: — отключение использование по умолчанию NULL SET ANSI_NULI._DFI.T_ON OFF — создание тавлицы для проверки CREATE TABLE testNULL ( id int } - - проверка значений EXEC Sphelp testNULL Здесь создается таблица testNULL (проверка значения NULL) с одним столбцом id (идентификатор) типа int, а затем проверяется возможность использования значения NULL.
Этот запрос возвращает Column_name (имя столбца) [...} NuHable (возможность ШЩ id по (нет) Обратите внимание, что значительная доля выходных величин sp_help Выла удалена по причинам недостатка места. Функция sp_help возвращает информацию о таблице, столбцах и ограничениях. С другой стороны, если взять: I — изменение установки по умолчанию SET ANSI_RVI.L_DFI.T_OK ОН -- удаление таблицы и паяторное свидание DROP TABLE testtWLL GO CREATE TABLE testWtJLI, ( id int > ЕлйС sp_help testWObb ... то получим Coiumn_name [...I Nuiiabie «Ь S-=- — =— — * — — — — SS S = S К = S — «S S3 9 = — — — —— = 5 = SSSS-S-—--—-- — —‘--ssss id ... yes Вычисляемые столбцы Теперь, когда мы представили типы данных, кратко рассмотрим вычисляемые столбцы. Они — действительно свежая особенность. которая выла довавяета в SQL Server 7.0. Странно, что они не выли реализованы в программе Enterprise Manager, которая просто прекращает работать, когда вы делаете любые изменения в таблице, так что вы были должны использовать ее е оригиналом таблицы. В SQL Server 2000 эта особенность должным образом поддержана. Ц <имя_столбца> AS <»ычисленнае_опредвлвиие> Вычисляемые столбцы могут использоваться, чтобы взять нечитабельные данные и перевести их в удобочитаемый формат в определении таблицы. В SQL Server 2000 эти столбцы теперь могут быть индексированы, так что они даже более полезны. Пример, удобный для СУВД, который я создал, предназначен для организации группировки по дням, месяцам и годам. В следующем коде мы имеем пример, который довольно близок к этому. Он группирует на секундам, чтобы сделать пример более легким для проверки: I CREATE TABLE calcColumns dateColwm datetime, dateSecond AS datepart (second, dateColum), -- вычисляежй столбец >
DECLARE gi int SET 81 = 1 WHILE (Si < 1000) BEGIN INSERT INTO fialcColumns (dateColumn) VALUES (getdateO) SET gi » 8i + 1 END SELECT dateSecond, max(dateColumn) AS dateColumn, count 1*1 AS countstar FROM calcColuans GROUP BY dateSecond ORDER BY dateSecond Здесь создается таблица aalcColumns (подсчет столбцов). содержащая два столбца: dateColumn (столбец дат) типа datetime и dateSecond (секунды даты) вычисляемый столбец, в который помещаются секунды из даты первого столбца. Организуется цикл размещения 1000 текущих дат (которые в данном случае отличаются только секундами и далями секунды), Далее данные, группируются по значениям вычислммаеа столбца и упорядочиваются по этим же- значениям вычисляемого столбца. После этой группировки для каждой группы формируется- величина секунд (dateSecond), максимальное значение даты (dateColumn) и число элементов в группе (councstar). Когда этот код выполняется, оя возвращает следующее: DateSecond dateCcHumn countStar и-кввья а» в » = e-fr« е-« = m я»я»3»-в-зЕ-язяе-wi 20 2000-11-14 11:07:20.993 8 21 2000-11-14 11:07:21.993 900 22 2000-11-14 11:07:22.993 91 Довольно опасная особенность с вычисляемыми столввдми заключается ж том, что таи будут игнорироваться, когда вы вставляете данные, если при этом опускаете список вставляемых полей. SQL Server будет игнорировать любые вычисляемые столбцы и относиться к этим полям, как будто они не существуют. Например, мы создаем следующую таблицу: create table testCalc value varchar।13), - valueCalc AS UPPER(value), values varchar(lQ) 1 Здесь создается таблица testCalc (проверка вычислений ) с тремя столбцами: value (значение-) типа varchar (10>, valueGalc (вычисляемое точение) — вычисляемое поле, в которое помещается знамение первого паля, написанное прописными буквами, и третье поле valued (значение 2) типа vat char (10). - Далее мы создадим некоторые новые значения Веэ использования списка ввода: i INSERT INTO testCale --ииям »десь иметь (value, value2) VALUES ('test', 'tests')
При этом никаких ошибок не произойдет. Если мы выполним запрос: I SELECT * FROM testCalc ...ан возвратит следующие результаты: value valueCalc valueZ test TEST testa Независимо от вычисляемых полей плохо кодировать оператор INSERT без списка ввода. Вычисляемые столбцы — чрезвычайно ценная особенность, и должны волновать любого, кто когда-либо должен выл неоднократно печатать и перепечатывать вычисления столбцов. Служебные столбцы Не каждый столбец будет или должен быть обозначен на логической модели данных. В этом разделе мы обсудим некоторые из причин, почему мы имеем столбцы, которые не соответствуют образцу столбца логической модели, но которые требуются в физической реализации. Основная особенность использования служебного столбца в том, что ои не является атрибутом сущности, которую моделирует таблица, Другими словами, столбец требуется, прежде всего, для целей реализации. Имеется несколько ситуаций, когда такие столбцы необходимы. Управление параллелизмом Когда имется ВД с больше чем одним пользователем, мы должны реализовать некоторую форму механизма управления параллелизмом. Мы должны быть уверены, что как только пользователь получает запись, которую он хочет изменить, никакой другой пользователь не может сделать изменения в записи, иначе мы иожем потерять информацию. Имеются два различных метода для выполнения этой задачи. □ Пессимистическая блокировка — она реализуется с помощью блокировки любых записей, которые пользователь читает с намерением модернизировать. Такая блокировка называется пессимистической, потому что првдагодагает, что другие пользователи будут, вероятно, пробовать редактировать ту же самую запись, что и текущий пользователь. Это — очень строгое условие аЯыгркяитц и обычно не нужно, так как в большинстве случаев никакие два пользователя, вероятно, не будут смотреть ту же самую запись в то же самое время. Мы бы использовали пессимистическую блокировку только тогда, когда пшшостыо необходимо заставить других клиентов ждать, пока мы не закончим работать с записью перед тем, как разрешим им получить доступ к ней. Проблема состоит в том, что мы должны заблокировать запись таким образом, чтобы никто другой не мог просматривать ее, или любой другой пользователь мог обратиться к нашим изменениям только после того, как мы снимем нашу блокировку.
□ Оптимистическая блокировка — это гораздо более популярное решение. Чтобы реализовать механизм оптимистической блокировки, мы просто добавляем столбец к каждой таблице, который изменяется каждый pas, когда изменяется строка в БД. Например, пользователь № 1 извлекает строжу значений. Вскоре пользователь № 2 извлекает ту же самую строку. Пользователь М 1 обвовляет запись, и значение столбца блокировки изменяется. Теперь, если пользователь № 2'попытается изменить строку, SQL Server проверяет значение столбца блокировки, вадит, что ояо изменилось, и предотвращает обновление пользователем М 2: Пользователь№ 1 Извлекает запись Модифицирует запись реи»—•> ——------1----------1---------!----------1--------— Извлекает запись Пытается модифицировать запись, но ему запрещено Пользователь № 2 Реализация оптимистической блокировки — довольно простая задача, но заметьте, однако, что каждый процесс, который использует таблицы, должен твердо придерживаться правил оптимистической блокировки. Если один из процессов игнорирует его, весь процесс терпит неудачу. Имеется несколько способов осуществить оптимистическую блокировку: □ Использование столбца типа timestamp — как было ранее рассмотрено, столбец timestamp является специальным столбцом, который изменяется каждый раз, когда изменяется строка. Столбцы timestamp — идеальный способ для создания оптимистической блокировки. □ Использование даты последнего обновления и/или последнего корректирующего пользователя — в основном это тот же самый метод, за исключением того, что мы добавляем столбец, чтобы хранить пользователя, который произвел последнюю модифицированную запись, и время корректировки. Мы делаем это е иоммщ»ю триггера, работающего с операторами INSERT и UPDATE. Этот метод лучше в той смысле, что даст удобочитаемую для человека величину, но хуже, потому что требует дополнительное кодирование триггеров. □ Использование всех столбцов в выражении WHERE во время модификации .— это метод, обеспечиваемый программными средствами, наподобие Microsoft Access, где нет столбцов типа times tamp. Обычно, когда мы выполняем оператор обновления, то включаем каждый столбец в таблицу в выражении WHERE. Если значения изменились с того момента, когда мы извлекли их, то это означает, что кто-то еще, вероятно, обратился к нашим данным при нас. Следовательно, оптимистическая блокировка зафиксирует отказ в работе.
Мы будем использовать столбец ti®estamp, так как его весьма легко реализовать, и он не требует абсолютно никакого кода. Итак, мы создадим следующую таблицу: CREATE TABLE te в tOpt intis tit Leek ( id in; MOT MOLL, value varchar 130J NOT NCLL autoTimestamp timestamp NOT NOLL, primary key (id) -- оптимистическая блокировка —• задает первичный-ключ в столбце ха Здесь создается таблица testOptimieticLock (проверка оптимистической блокировки) с тремя столбцами:- id (идентификатор) — типа int, он, же является и первичным ключом, value (значение) типа varchae(30} и avtoTimastamp (автоматическая отметка времени) типа tiue-sterp который является оптимистическим ключом. Значения всех столбцов не могут быть NULL. Далее мы выполним следующий фрагмент. Первым шагом является создание новой строки в столбце и получение значения оптимистической блокировки дли строки. INSERT INTO testOptimistieLock (id, value) VALUES (1, 'Тест-1'1 DECLARE itiraestasipHcild timestamp SELECT etimestampHold - autoTimestamp FROM tesuOptimisticLock WHERE value - 'Тест 1' — сначала будаи работать/ UPDATE testOptimisticLock SET value - 'Новое значение' WHERE id - 1 AND aucoTLmestamp “ gtimestampHold IF eOrowcount » 0 BEGIN raiserror 50000 'Строка изменена другим пользователем' END SELECT id, value FROM testOptimisticbock -- затем, усглноеиа отказ в работе UPDATE testOptimisticLock SET value - 'Второе новое значение' WHERE id - 1 AND autoTimestanp - вtimeatattpHold IF ggrowcount - Й BEGIN raiserror 50Q&0 'Другой пользователь изменил строку' END SELECT id, value FROM teatOptiffilsticLock Здесь в таблице testOptimistioLock создается строка с параметрами id — 1, value = “ "Тест I". Далее объявляется переменная gtintestampHoId (фиксация значения типа timestamp) и в нее помещается значение поля autoHmestaago, Зля введенной строки таблицы testOptimisticLock (она содержит в поле value значение "Тест 1” (.Далее выполняется
часть запроса, которая имитирует нормальную работу. Здесь в строку /единственную; таблицы testOptimistocLock, у которой id“ 1, в поле value помещается. значение "Новое значение", если только поле- autoTimestaisp не изменило свое значение (а ано в данном случае не изменило своего значения, потому что строка не изменялась}. Вели бы изменение пом val ие не произошла (вв данном случае ано произошло}, значение глобального параметра ffgrowcount, определяющего число изменённых строк, стало бы равным нулю (что возможно, если параметр autoTiiaestamp_ изменит свое значение, означающее., что строку изменил другой пользователь в течение рассматриваемого промежутка времени}, будет, выдано сообщение "Страка изменена другим пользователем". Этот фрагмент завершается выводом значений полей id и value для их контроля. Наконец, выполняется часть запроса, которая имитирует отказ в обновлении. строки. Эта часть аналогична только что рассмотренному фрагменту, но в этом случае в иоде value помещается значение "Второе навое значение", а в случае неудачи е изменением строки выводится сообщение “Другой пользователь изменил строку". В этом фрагменте реализуется именно этот случай, так как строку не удается скорректировать, потому что до этого в предыдущем фрагменте было изменено значение поля autoTimes ta/np ввиду успешного обновления строки. И этот фрагмент, завершается выводам значений полей id и value. Этот запрос возвратит id value 1 Новое значение Server: Msg 50000, Level 16, State 1, Une 38 Другой пользователь изменил строку- id value 1 Новое значение Устройства блокировки записей Созданный мной служебн ый столбец, который важен во многих приложениях. является устройством блокировки записей. Например, возьмем таблицу домена,-который определяет тип некоторой записи, скажем, contactType (тип партнера), как на следующем рисунке: contactType contact i сшас'Ту petti | - ' - coniactid________________ I | name ~ - ------------------------• contact lypeld (F K) i tdtsablfcFi - - ...__________________ I - Здесь contactType — таблица "тип партнера"; contactTypeld — идентификатор типа партнера; name — название типа; disable?! — флаг запрета; contact— таблица "партнер"; contacted — идентификатор партнера. В таблице contactType мы имеем столбец типа bin, названный disable?!. Если этот столбец имеет значение 1, то мы больше не сможем использовать это значение как тип : партнера. Нам не нужно удалять «и изменять любые существующие -значения партнера, чтобы установить этот флаг. (Обратите внимание, что это решение будет требовать, чтобы триггер обеспечил данное правило дая проверки-значения contactType. disable?!, так как это — правило, устянонленное между таблицами,) 11--1868 303
Почему нам может потребоваться это сделать? Имеются три очевидных проблемы, которые мы должны решить: Q Если бы мы имели только 10 партнеров, не было вы проблемы физически удалить левые строки в таблице contact, где используется конкретный экземпляр eontactType. Однако если мы имеем 1 000 000 партнеров, все партнеры должны выть проверены. Это могло вы быть чересчур много. □ Так как пользователь может и не предвидеть любое дальнейшее использование конкретного значения, нет никакой причины полностью удалять запись ив БД. □ Могут существовать записи-потомки, которые иы не хотим удалять из ВД. Обратите внимание, что когда список значений eontactType представляется пользователю, они должны быть отфильтрованы следующим образом: I SELECT паше FROM eontactType WHERE disableFl - 0 ЗЗееь us таблицы aontactType выбираются записи (а в этих записях поля с именем пате), в которых флае disableFl равен нулю. Это подразумевает некоторый компромисс с точки зрения использования запроса, так как и таких случаях нам нужно балансировать затраты на удаление со стоимостью запроса. Мы могли бы таки» реализовать флаги удаления и запрещения записи, не ррпуская параллелизм для нашей БД и удаляя строки позже ночью, когда все спокойно. Следует уиомянуть, что disableFl — один из тех атрибутов, который требует удовлетворения 4НФ, поскольку мы будем, вероятно, хотеть знать не только о самом факте запрета, но и, например, когда появился этот запрет. Выла вы можно, конечно, добавить таблицу contactlypeStatue (состояние типа партнера), где мы контролируем предыдущее и текущее состояние. На практике это редко необходимо, но должно, конечно, быть рассмотрено в каждом случае отдельно, когда предварительно вы спрашиваете, когда и почему атрибут eontactType, имеющий значение "Активный", выл запрещен, но не можете получить простой ответ и находитесь в затруднении. Сопоставление (порядок сортировки) Последовательность сопоставления для SQL Server определяет, как символьные данные будут организованы при хранении, как они будут при необходимости сортироваться, и как будут сравниваться. SQL Server ж Windows обеспечивают огромное число типов сопоставления, на которых можно выбирать. Чтобы увидеть текущий тип сопоставления для сервера и БД, вы можете выполнить следующие команды: I SELECT serverproperty(‘collation') SELECT databasepropertyex('master*, 'collation') В большинстве систем, установленных для англоязычных стран, тип сопоставления по умолчанию — SQL_LMIM1_SB1№RAL_CPI_CX_AS, где Latinl_General представляет нормальный латинский алфавит, СР1 — обращение к кодовой странице 1252 (установленный по умолчанию набор символов Latin 1 ANSI для SQL Server), и последние части представляют нечувствительность к регистру и чувствительность к ударению соответственно. Полный перечень всех типов сопоставления можно найти в документации по SQL Server 2000.
Для России используется значение Cyrillic__General для перевес параметра. Прим, персе. Чтобы просмотреть список всех параметров сортировки, установленных иа данном SQL Server, вы можете выполнить следующий оператор: SELECT * FROM ::fn_helpaollations(J На компьютере, который я использую для испытаний, этот мпрое возвращает около 700 строк, но обычно нам не нужно будет изменять значения и© умолчанию, которые адмииистрятор БД первоначально выбрал, Имеется несколько важных причин задавать различные сопоставления. □ Чувствительность к регистру — в зависимости от приложения может иметься потребность обращаться к коду и данным как чувствительным к регистру. Это может потребоваться от случая к случаю. Обратите внимание, что это делает ваш код и имена объектов также чувствительными к регистру. Помните, что чувствительность к регистру вызывает трудности при поиске. Например, при поиске всех имен, начинающихся с букам "А", мы валяемы искать все элементы, начинающиеся с "А" или "в". Мы могли использовать функции преобразования к верхнему регистру для столбцов или переменных, но ото нарушает индексацию и обычно — не лучший вариант, □ Сортировка иностранных символов — другая причина — это иностранные символы. Используя Unicode, вы можете использовать любой символ любого языка мире. Однако очень немного языков используют тот же самый A-Z набор символов, который используют англоязычные страны. Фактически, почти все другие европейские языки используют акцентированные символы, которые неямвются частью набора 7-битовых ASCII-символов. Возможно использование различных типов сравнения при реализации столбцов на различных языках. Ми рассмотрим пример этого позже в данном разделе. В предыдущих вервиях SQL Server весь сервер имел единственный порядок сортировки. Это подразумевало, что в каждой БД, в каждой таблице, в каждом поле, в каждом запросе вы были вынуждены использовать один и ют же порядок сортировки. Эго приводит к мзгт-ересмьш трудностям, например, при попытке реализовать сортировку, чувствительную к регистру. Однако в SQL Server 2000 мы можем задавать сопоставление как для нашего сервера и ВД, что упомянуто ранее, так и для столбца, и даже для оператора ORDER или SELECT. Чтобы установить последовательность сопоставления для столбцов типа char, varchar, text, nchar, nvarchar или ntext при создании таблицы, вы задаете ее, используя оператор COLLATE в определении столбца, подобно следующему: I CREATE TABLE otherCollate ( id integer IDENTITY, nawe twarchar (30) NOT NOLL, frencMame nvarcharOO) COLLATE French_CI_AS_WS NULL, spanishKarre nvarchar (301 COLLATE Mjdern_Spanish^C'__Ai_WS NULL I Здесь создается таблица otherCollate (другие сравнения). содержащая четыре столбца: автоинкрементный столбец id (идентификатор) типа integer; name (имя) типа лvarchar (30), который ме может использовать значение HULL; frencMlame (французское
имя) типа nvarchar (SO), использующий французское сравнение, допускающий значение NULL, и spanistMaiBe (испанское имя) типа nvarchar(30), использующий модернизированное испанское сопоставление. допускающий значение NULL. Теперь, когда мы сортируем столбец f renchName, то сортировка будет нечувствительной к регистру, но организует строки в соответствии с французский набором символов. То же самое используется с испанским набором относительно столбцаspanishUame. В следующем примере мы рассмотрим другое свежее использование ключевого слова COLLATE. Можно использовать его, чтобы воздействовать на сравнения, выполняемые в выражении. Мы делаем это, изменяя одао или оба значения с обеих сторон выражения в двоичном сопоставлении. Создадим таблицу, называемую collateTest (тестирование упорядочивания): I CREATE TABLE collateTest t name varchar(20i COLLATE SQL_Latinl_G«neral_CFl_CI_AS SOT NOLL ) INSERT INTO collateTest (nameI - - VALUES ('BOB'! INSERT INTO collateTest (cam) VALUES ('bob') Здесь создается таблица collateTest, содержащая один столбец с именем пале (название) типа varchar (20), в которую помещаются две записи 'ВОВ' и *ЬоЬ*. Обратите внимание, что для целей демонстрации оператор COLLATE, который ж включил, используется по умолчанию для моего сервера. Это, вероятно, будет и вашим сопоставлением, если вы используете сопостаилеиие по умолчанию (мы примем это сопоставление для остальной части книги). Тогда можно выполнить следующий оператор иад ВД; I SELECT пале L FROM eellateTest’ ИЯЕИЕ name - 'BOB' ... который возвратит обе строки: пате bob ВОВ Однако если мы изменим сопоставление для символов "ВОВ“ и выполним его: I SELECT name FROM collateTest WHERE name - 'BOB' СОЫАТЙ Latinl_General_BIK то оператор вернет только одну строку, которая точно соответствует символам "Ьов": name BOB
Вы должны были заметить, что мы приводим только скалярное значение'ВОб" для - бинарного сопоставлении. Определение еопостанления в первой чг.сти сравнения может быть хитрым вопросом, аа мы не будем ага рассматривать здесь. Вообще, ::умпп> лобани-н. функцию COLLATE is обеим сторонам выражения при выполнении.такого действии, В нашем случае вместо применения еопостанления только к скалярной величине мы написали бы это следующим образом: .... .„., I SELECT name FROM collateTeSt - ’ : :: WHERE name COblASS L*tXal_Gmsal_BXN - 'BOB' COLLATE Lacir.i_General_BIM He будем глубже вникать в предмет сопоставления. В документации по SQL Server имеется большое количество информации относительно сопостамеиия, включаяправ и да для ' сопоставления предшествующего элементе, - ' г=- --- =- - =- л.л- Ключи Как было рассмотрено в.главах, посвященных логическому моделированию. определение ключей — одна, из наиболее важных задач в ’проектировании ВД. В.этом.раздала мы не будем рассматривать, почему мы определили выбранные ключи, .а скорее, как иы рдализуем их. Имеется несколько различных типов ключей, которые мы уже рассмотрели; □ Первичный — содержит первичный указатель на строку в таблице. □ Дополнительный — содержит дополнительные умазатега на строку в таблице, ’ обычно любые уникальные условия, которые, как мы можем удостовериться, : представляют один или большее количество столбцов таблицы. - г 3 Внешний — внешние ключи — указатели на первичные ключи в других таблицах. Первичные и дополнительные ключи гибридные объекты — частично ограничения, частично индексы. Ограничения объявляют, что для объекта должен быть истинным некоторый фактор. Для ключей это означает, что значения в таблице должны быть - - уникальны.. - :: „ Объявления ограничений ключей реализуются так же, как индексы. Если вы йе понимаете, как SQL Sewer хранит данные и^еализует индексы, пожалуйста, прочитайте следующий раздел. Это жизненно важно для вашего понимания физического моделирования и вашего использования индексов и ключей, не говоря уже о понимании того, почему мы нормализуем наши ВД. Снова много информации содержится в "Professional SQL Server 2000 Programming" Роба Виейра (Bob Vieira / (Wrox Press. ISB№-l-B6lQQ4-M-6f. Индексы Любое обсуждение того, как строить ограничения, следует начать с обсуждения основ индексов и структур таблиц. Индексы —.некоторые из наиболее важных объектов, входящих в физическое моделирование. Мы используем их чтобы реализовать первичные ключи и вторичные ключи (уникальные ограничения), и межам использовать их, чтобы улучшить характеристики ВД. Построение индексов будет рассмотрено в главе 14, Понимание, как работают индексы, даст нам основу, чтобы определить, какие виды индексов использовать. Мы дадим только краткую трактовку того, как SQL Server реализует индексы, поскольку эта сложная тема вне возможностей данной книги.
Основные структуры индексов ТЛя^вхял у^я^яжп поивк строк в таблице, и обычно, хотя и же всегда, задают структуры, связанные с таблицей. Они строятся, используя один или большее количество столбцов таблицы и создавая специальную структуру для таблицы на основе этих выбранных ключевых столбцов, Имеются два различных класса индексов: □ Кластерные — упорядочивают физическую таблицу в соответствии с порядком индекса, □ Неклаетерные — полностью отдельные структуры, которые просто ускоряют доступ, Мы можем иметь уникальный тип индексов любого класса. Уникальные индексы используются не толю, чтобы ускорить доступ!, но и обеспечить уникальность элементов столбца или группы столбцов. Причина того, что они используются для этой цеди, заключается в том, что для определения уникальности значения ш должны просмотреть его в таблице, и так как индене иеполыуется для ускорения доступа к данным, он отлично подойдет. Имеются другие назначения, но они — вне возможностей этой книги (просмотрите тему Create Index (создание индекса) в SQL Server 2000 Books Online, где создание индексов рассмотрено в деталях). Я здесь представлю только основные параметры, которые подходят для проектирования физических структур. Дополнительные настройки, связанные с функционированием, как и некоторыми другими оеовенноетями, хотя и являются удобными, но вне наших возможностей. Индексы реализуются, иоиммуя структуру сбалансированного дерева, также называемую "В-дерево” (В-Tree), На следующей диаграмме мы даем основную структуру тина. В-дервва, которую SQL Server использует для индексов. Каждый из внешних прямоугольников — страница индекса объемом в 8 К, которую SQL Server использует для любого хранилища данных. Первая страница показывается наверху диаграммы: A J В
Мы имеем три значения, которые являются ключами индекса. Чтобы решать, по какому пути следовать к более низкому уровню индекса, мы должны решить, между какими ключами находится значение: от А до I, от J до-Р вли больше, чем Р, Если аиачение -— между А и I, мы идем по первому маршруту до следующего уровия; .; .. да с в Мы продолжаем ету процедуру да уяяов-лиетьев индекса, У злы-листья будут рассмотрены, когда мы переДдеи в овеуждеиию деталей кластерных и некластерных индексов. Эта структура характерна для реализации всех индексов SQL Server. Однако следует отметать одну важную деталь. Мы сказали, что каждая из этих страниц — 8 К в размере. В зависимости от размера вашего ключа (определяемого суммой длин данных столиц» в ключе, максимально до 900 байтов^, вы можете иметь где-то от восьми входов до более чем одной тысячи иа одной странице. Следовательно, ми деревья вообще не растут в глубину, гели разработавы должным образом. В нашем примере мы использовали упрощенный набор значений индекса, чтобы показать, как дерево индексов разворачивается. В следующем примере построим основную таблицу и основные индексы. Ми будем использовать вту таблицу в оставшейся части наших примеров данного раздела. CREATE TABLE testindex ( id int IDENTITY, firstNan* verchar(20), middlsName •archer 120 >, laetNaae varchar(30) . ) CREATE INDEX Xtestlndex ON testindex(id) CREATE INDEX Xtestlndexl ON testindex(firstNane) CREATE INDEX Xt»*tlnd«2 OS testIndex (MddlaSamH CREATE INPEX XtestIndex! OS testindex (IsstNaire) CREATE INPEX Xtestlndex4 OR test Index (f trstNare, mlddleName r lartitemj Здесь создается таблица test Index (тестирование индекса), содержащая четыре столбца: автоинкрементный столбец id (идентификатор) munairtt, firstName (имя) типа vacchar (20), aiMletlaaa (отчество) типа varchar(2Gj в lastXacte (фамилия) типа v-атслвг (30), Далее создаются 5 индексов с именами Xtestlndex, Xtestlndexl * Xtestlndex4. Первый содержит столбец id, второй — столбец firstl-larce, третий — столбец middleNa^e, четвертый — столбец lascNa~e и пятый — столбцы firstxame, жжвШеявже и lastMa/яе. - -- -- - - Если все нормально, оператор будет успешно выполнен. Чтобы удалить индекс, вы иетшаиувте еявдоещв* оператор: Ц DROP index testIndex.xtestIndex - - -
Кластерные индексы Узлы-лиетья -клаетернотс индекса фактически яйлйются страницами данных. Следовательно, может быть только сыпи кластерный индекс для таблицы. Точная реализация страниц данных — не особенно’ важная деталв; скорее, достаточно понята, что данные в таблице физически упорядочены в порядке индекса. При выборе, какие поля использовать как базу дли кластерного индекса, лучше принять удобочитаемый для человека индекс. Другими словами, если ваша таблица имеет характерное упорядочивание, которое будет иметь смысл с точки зрения человека, это — хороший кандидат для кластерного индекса. Возьмем, например, словарь. Каждое слово в словаре можно рассматривать как строку в ВД, Тогда эта ВД кластеризована словами. Наиболее важная причина этого —- то»- что- мы часто должны просматривать группу слав- и их значений, чтобы видеть, нашли ют кы то, что нужно. Тан кан ясе данные сортируются в юсоетаетстаии со словами, мы можем просматривать слова на страницах вез каких-либо затруднений- Мы используем почти такую же логику» чтобы решить, нужно ли использовать кластерный индене, = !- •- - - ".: : - Кластерные индексы даяжиы использоваться для: □ ключевых наборов, которые содержат ограниченное число различных значений -— так как если данные сортируются ключами, все просматриваемые величины при поиске по значениям индекса мы видам немедленно; - - □ запросов диапазонов — получение всех данных в последовательности обычно имеет смысл, когда вы должны получить диапазон, скажем, от "А” до "F"; Q данных, к которым обращаются последовательно, — очевидно, что если вам нужен доступ к данным в этом порядке, то имея уже отеортироваиные данные, выполнение будет существенно улучшено; □ запросов, которые возвращают большие наборы результатов, — этот момент будет иметь больше смысла в сравнении с некластерным индексом, но расположение - данных непосредственно на узлах-листьях структуры уменьшает непроизводительные издержки; □ наборов ключей, к которым часто обращаются с помощью запросов, использующих операторы JOIN или GROUP BY, — часто от могут вить столбцами внешнего ключа, во не обязательно. Операторы GROUP ВУ всегда требуют, чтови данные были отсортированы, отсюда следует, что наличие отсортированных данных так или иначе улучшит работу. Так как кластерный индекс физически упорядочивает таблицу, добавление новых-значений будет -обычно вынуждать вас помещать новые данные в середину таблицы — единственное исключение из этого, если вы используете кластерный индекс е автоинкрементным типом полей. Если новая строка не будет умещаться на странице, SQL Server должен разбить страницу (что проиеходат, когда страница полная, так что половина данных отделяется на новую страиищ- — следовательно, происходит и давамяше имена). Разбиения страницы — доиодаво дорогостояще действия и вредят райо», так как даниые на будут распсложены на последовательных страницах, не давая возможность синхронного чтения даековой подсистемой. Если вы не имеете набора ключей, которые удовлетворяют одаому из критериев кластерного индекса, обычно лучше всего не строить его. Также важно не использовать кластерный индекс на столбцах, которые часто изменяются. Так как таблица упорядочена по значениям в столбце, SQL Server, вероятно, будет вынужден перестраивать строки на своих страницах, что является дорогостоящим действием. Это особенно важно, если вы должны модифицировать значения ключа, которые в свою очереда изменяют большое количество различных строк,
Другой важный момент, на который следует обратить внимание, — то, что вы должны - - иметь кластерных ключей как можно меньше, так как они будут использоваться в каждо м некластерном индексе (как мы увидим дальше). Так что, если вы изменяете- кластерный ключ, вы добавляете работу и для всех некластерных индексов также. Для кластерной таблицы локатор строк (см. ниже) — всегда .кластерный индексный ключ. Если кластерный ключ не уникален, SQL Server добавляет случайное значение, чтобы сделать его уникальным. Некластерные индексы Некластерныи индекс аналогичен индексу в «кете книги, когда книга индексируется номером страницы. Как ив индексе книге, некластерный индекс полностью отделен от отрок данных. , - ы Узел-лист структуры некластерного индекса содержит только указатель на отроку данных, содержащую основное значение. Указатель из некластерного индекса на .«року даякых.. называется локатором строк. Структура локатора строк зависит от того, имеет ли таблица кластерный индекс или нет. Верхние уровни индекса — те же самые, что и у кластерного индекса. Мы рассмотрим диаграммы, показывающие различные локаторы строк, несколько позже после обсуждения основ некластерных индексов. Мы обсудили таблицы с кластерными индексами они называются кластерными таблицами. Если нет кластерного индекса, таблица называется "кучей' . Мое собственное определение "кучи** — "группа вещей, помещенных иди положенных друг на друга". Это хороший способ объяснить, что происходит в таблице, если нет кластерного индекса. BQL Server просто помещает каждую новую строку в конец таблицы. Обратите также внимание, что, если вы производите удаление, SQL Server просто выбросит строку, не используя повторно место, Однако если вы проводите обж»даЯяВ|Эмежияв.дае возможности: □ обновление на месте — значения в строке просто изменяются; □ удаление н вставка — вся строка удаляется из таблицы я затем повторно вставляется. . ~ _ Имеется несколько критериев, которые SQL Server использует, чтобы определить, действительно ли следует выполнить обновление строки. Два наибол« частых включают: □ Наличие у таблицы триггера AFTER, выполняемого после обновления, — когда вы имеете триггер для таблицы, который работает после того, как строка обновлена; строка сначала удаляется, а затем вставляется. □ Попытка вставить большее количество данных, чем может разместиться на странице, —> если вы имеете данные переменной длины в вашей таблице, и пытаетесь увеличить размер размещенных данных,^!» Server должен будет удалить строку и повторно вставить ее где-нибудь щЦй, возможно, над ругой странице. Эта процедура отличается от случая, когда имеется кластерный индекс, потомучя теперь нет никакой причины разбивать страницу в середине структуры» - быстрее и легче переместить данные к концу" кучи”. : = Теперь нам нужно рассмотреть два различных типа таблиц и как реализуются их векластершле локаторы-етр«,- - - -
"Аучэ" Для таблицы типа "кучи" без кластерного индекса локатор строк является указателем на страницу. которая содержат строку. Предположим, что мы имеем таблицу по имени ItemTable (таблица элементов), с кластерным ивдекоом, состоящим из столбца description (описаиие), и ивкластерным индексом, состоящим из столбца itemld. В следующей диаграмме мы видам, что для того чтобы найти itemld 3 в таблице, нужно ваять летую ветвь дерева, потому что 3 находится между 1 и 20, затем следовать вниз ао пути между 1 и 10. Как только ми добираемся до страницы, то имеем указатель на страницу, которая еодератег.иуявое значение. Предположим, что это* указатель состоит из номера страницы и смещения на странице (страницы нумеруются с нуля, а смещения нумеруются е 1), Наиболее важный момеит относительно этого указателя заключается в том, что он приводит непосредственно к сороке на странице, которая содержит значения, которые мы ищем. Это не так, как в случае, когда мы работаем с кластерной таблицей, и важно понять различие. Инда® в столбце Itemld Таблица ItemTable таблицы ItemTabis.. Некластерные индексы а кластерной таблице В этой ситуации, когда вы прошли яеклаежвдиыа индекса достигли его узла-листа, вместо получения указателя, который приводит вас иевосредатаянжо к данным, вы получаете кластерный индекс, который вы должны пройти, чтобы получить данные. На следующем графите нижний левый прямоугольник — некластерный индекс, как в нашем предыдущем примере, аа исключением того, что указатель иаменилея от определенного местоположения к значению кластерного ключа. Далее мы используем кластерный ключ, чтобы пройти кластерный индекс и добраться до данных.
Издержки этой операции минимальны, и она "теоретически" лучше, чем использование прямых указателей на таблицу, потому что требуется лишь минимальная реорганизация для любой корректировки кластерного индекса или кластерного ключа. Когда корректировка выполнена, указатели Не перемещаются. Вместо этого, в таблице оставляется указатель, который указывает на новую страницу, .где теперь расположены данные. Все существующие индексы, которые имеют старые указатели, просто указывают на старые страницы, затем следует новый указатель на страницу с новым размещением данных. Уникальные индексы Уникальный индекс — второй наиболее важный момент, который вы определяете при реализации кластерного индекса. Вы можете создавать уникальные индусы как дан кластерных, так и для некластерных индексов. Это гарантирует, что среди индексных ключей не будет каких-либо двойных значений. Например, если мы создали следующие индексы в нашей таблице testlndex: , .. ,, CREATE UNIQUE INDEX Xteatlndexl ON tes^Index (laatHeme) CREATE UNIQUE INDEX Xtestlndex2 ON = HfirstNanw, «iddleNaai*, laatKeeeJ- (обозначения см. в предыдущих примерах) ... мы не могли бы ни вставить двойные значения в столбец lastName, ни создать вхождения, которые имеют то же самое сочетание значений firstNan», raiddleNarae и las-лате. Мы обычно не используем уникальные индексы, чтобы обеспечить уникальность вторичных ключей. SQL Server имеет механизм, известный как ограничение UNIQUE, а также ограничение PRIMARY KEY, которые мы будем использовать. Недольемте униии-даные- иадексы, когда вам яужио создать индекс для улучшения работы, : ...
Следует также отметить, что для функционирования ваших сметем очень важно, чтобы вы использовали уникальные индексы всюду, где только возможно, поскольку это увеличивает возможности оптимизатора SQL Server (SQL Server optimizer) предсказывать, скопко строк будет возвращено мировом, который использует индекс. В нашем примере, если мы попробуем выполнять следующий запрос:. I INSERT INTO .testlndex LfisstName, middleKame, LastName) VALUE® ('Louis', 'Banks', 'Davidson’I INSERT INTO testlrtiiex (firatMame, «iddleKame,. lasttfame) VALUES ('James', '..Banks' , 'Davidson’) ... то получим следующую оиивкуг I Server: Msg 2601, Level 14, State 1, Line 3 Cannot insert duplicate key row in object 'testindex' with unique index 'Xtestlndexl* . The statement hasjseen textain>tetf. (Сервер: Сообщение 2601, уровень 14, состояние 1, строка 3 Нельзя поместить строку с повторяющимся ключом в объект tes-tlmiex с уникальным индексом Xtestlndexl, Оператор удален.) Хотя сообщение об ошибке могло вы быть и лучше, ясно, что предотвращается помещение дубликата строки с той же фамилией, /GNOREJdUPJCEY Часто полезно задать уникальность индексов с помощью параметраTGMORE_DUP_KEY (игнорировать дубликаты ключей). Используя это, мы можем сообщить системе SQL Server, чтобы она игнорировала любые строки сдубликатами ключей, если мы так желаем. Вели мы изменим наш индекс'в предыдущем примере: IDROP INDEX testIndeaTXteetIndex4 CREATE UNIQUE INDEX Xtestlndex4 ON testindex(firstSame, middleName, lastName) WITH IGNORE_DU₽_KB’r Здесь сначала удаляется индекс Xtestlndexi для таблицы testindex и заново создается этот же индекс, но уже с параметрам IGNORE_DUP_KEY. ... а далее выполним тот же код, что и в предыдущем случае: INSERT INTO testindex (fiTStNaKse, middleNaM, lastNaae) VALUES ('Louis’, 'Banks', 'Davidson') INSERT INTO testindex (firstNaine, middleName, lastName) VALUES ('James', 'Banks', 'Davidson') ... мы подучим следующий результат: Server; Msg 3604, Level 16, State 1, Line 3 Duplicate key was ignored. (Сервер: Сообщение 3604, уровень 16, состояние 1, строка 3 Дубликат ключа проигнорирован.)
Эта настройка, вероятно, не то, что вам нужно в большинстве случаев, но может оказаться удобной, когда вы строите копию некоторых данных. Вы можете в этом случае вставлять данные, не волнуясь о том, что выло туда помещено прежде, поскольку индекс отбросит двойные значения. Обратите внимаши, что это не работает при овновлениях, а только при вставке строк. Первичные ключи Выбор первичного ключа — один из наиболее важных выборов, которые мы делаем для конкретной таблицы. Он является первичным ключом, который будет мигрировать в другие таблицы как указатель на конкретное значение. В логическом моделировании мы выбрали использование указателя в виде 4-байтового целого числа для первичных ключей сущностей, которые мигрируют к зависимым таблицам-потомкам следующим образом: parent г*---"----1 pare nil а | child grandchild . childkl \ . childld г К! ' —• parenild f К)[ ——< paremid('K) grandCtiiHltf Здесь parent — таблица “предок': parent Id — идентификатор предка; child — таблица "потомок"; childld — идентификатор потомка; grandohiId — таблица “потомок потомка"; grandchildld — идентификатор потомка потомка. В физическом моделировании это заставляет реализованные ключи становиться довольно большими, не столько в смысле размера данных, сколько в смысле большого числа значений, которые мы должны использовать. Другая проблема е реализацией первичных ключей, таким образом, заключается в том, что мы обычно завываем тот факт, что ключ таблицы не должен состоять только из служебных столбцов, Мигрирующие ключи хороши, поскольку они представляют значения табсиды. из которой они пришли, но, добавляя уникальное значите, мы по существу сделали его служебным ключом (так как независимо от того, что вы добавляете к служебному значению, вы будете всегда получать служебное значение). В логическом моделировании это представление таблиц-предков и' - таблиц-потомков показало нам, что вы не могли иметь потомка потомка (внука) вез потомка (ребенка) или предка (родителя), и мы всегда моделировали вторичный ключ, чтобы было ясно, что понимается под вторичным ключом. Однако теперь, находясь на физической стадии моделирования, мы будем следовать несколько иной стратегии. Каждая таблица будет иметь единственный служебный первичный ключ, и мы реализуем вторичные ключи в наших таблицах, как показано в следующей диаграмме: parent psrentw eftflg crtW ч---------------------------------- Здесь допилнипгельна ocherParentAttForAltKey ™ другой атрибут предка дм вторичного ключа; otherChildAttrfbuteF&rAItKey ™ другой потомка дм вторичного ключа; otherSrandChildAttribnteForhltRey ™ другой атрибут потомка потомка для аяюричюю клм
Мы выберем этот метод по следующим важным причинам: □ Мы никогда не должны волноваться относительно того, что делать, когда значение первичного ключа иамеияетея. □ Каждая таблица имеет единственный столбец указателя первичного ключа, и в твл случае намного легче разрабатывать приложения, использующие этот указатель, потому что каждая таблица будет иметь едашственное значение дня первичного ключа. □ Наш индекс первичного ключ* будет очень мал, и таким образом операции, которые используют его для доступ* к таблице, будут быстрее. Большинство операций обновления и уделения будут, вероятно, изменять данные, обращаясь к данным с помощью первичных ключей, использующих йот индекс. □ Соединения таблиц будет более просто проектировать, так как все мигрирующие ключи будут содержать единственное поле. Имеются неудобства в этом методе, типа того, что при соединении таблиц всегда нужно выяснять, что фактически означает первичный ключ, не говоря уж о других соединениях, которые необходимы для доступа к данным. Так как цели нашей OLTP-системы состоят в том, чтобы иметь небольшие ключи, ускорять корректировки и иметь согласованные данные, эта стратегия не только приемлема, но и влагоприятна. Реализовать первичный ключ таким образом очень просто: I CREATE TABLE namelable id integer NOT HULL IDENTITY PRIMARY KEI OICLUSTSRED Здесь создается таблица e именем nameTable (имя таблицы) с единственным автоинкрементным столбцам, id типа integer, который не может иметь значений NULL, являющимся неклас мерным первичным нулем. Обратите внимание, что вы не можете иепольаовата столбцы, допускающие значения null в первичном ключе, даже если вы можете иметь столбцы, допускающие значения NULL в уникальном индексе. Причина этого на самом деле довольно прост*. Первичный ключ •— Идентификатор записи, a NOLL ие обеспечивает никакую форму идентификации записи. Обратите также внимание, что мы явно определили NONCLUSTERED (некластерный). Если мы опустим эту спецификацию, ограничения первичного ключа будут реализованы, используя кластерный индекс; иы часто ие вспоминаем этот факт, пока не будет слишком поздно. Следующий оператор будет также работать и в случае, если вам нужно определить больше чем один столбец для первичного ключа. CREATE TABLE nanwTable ( В id integer SOT NULL IDENTITY, В pkeyColumnl integer NOT NULL, В pkeyColumni integer NOT HULL В PRIMARY KEY NONCLUSTERED (id} | } Здесь создается таблица, с именем nameTable, содержащая три столбца; автоинкрементный столбец id типа in г еоег, являющийся некластерным первичным ключом, и два дополнительных столбца первичного ключаpkeyColumnl и pkeyColumnZ (первый столбец первичного ключа и второй столбец первичного ключа).
Обычно, когда мы реализуем первичный ключ как указатель в вида целого числа, мы не заинтересованы тратить на это впустую имя кластерный индекс, С первичным ключом этого вида яелмя обращаться как с каким-либо упорядоченным ключом. Причина этого в том, что вам редко будет нужно обращаться к строкам в порядке, определяемом первичны м ключом, или рассматривать диапазон строк на. основе первичного ключ*. Если требуется упорядочивание, лучше добавить дополнительный столбец, потому что если потребуется переупорядочить записи, вы не сможете изменить значение идентифицирующего столбца; кроме того, вы не должны будете часто изменять первичный ключ то каким-либо причинам. Как упомянуто выше, вели вы должны гарантированно обеспечить ключ, который будет уникальным по всей ВД или даже по всему серверу, то можно использовать столбец типа uniqueidentifier со свойством ROWGUIDCOL, подобно следующему: CREATE table пашвТаЫе ( id uniqueidentifier NOT NOLL ROWGUiDCOL DEFAULT newidO, PRIMARY KEY NONCLUSTERED (id> Обратите внимание, что ROWGUIDCOL ие обеспечивай ни уникальности, ни. значения по умолчанию, как в идентифицирующем столбце. Только использование при необходимости типа uniqueidentifier, поскольку он использует шестнадцать байтов вместо четырех для целого числа, даст это. Вторичные ключи Формирование вторичных ключей — очень важная задаче физического моделирования. При реализации вторичных ключей мы имеем два выбора: □ уникальные ограничения; □ уникальные индексы. В качестве примера возьмем таблицу патеТаЫе и расширим ее, включив имя и фамилию. При этом мы хотим обеспечить правило, что необходимо иметь униквльяые полные имена в таблице. Мы могли вы создать индекс, подобно следующему: I CREATE TABLE nameTable ( id integer NOT TOLL IDENTITY PRIMARY KEY NONCLUSTERED, firstNaiw varchar(15) HOT lastName varchaxflSj NCT tiULL ) CREATE BMIGUB INDEX ХпшйвТаЫв OK nameTable If irstNanw, last»aa»J Или можно использовать уникальное ограничение типа: I CREATE TABLE ПМЮТаЫв id integer NOT NULL IDENTITY PRIMARY KEY, firscNanie varcher(lS) NOT NULL, laetNaine verctar(15) NOT KULL, UNIQUE (firstNarae, LasiNatae) ) ai7
Хотя фактически оба они основаны на уникальных индексах, уникальные ограничения — велев предпочтительный метод реализации вторичного ключа и овеепвчения уникальности. Это потому, что ограничения семантически предназначены для обеспечения ограничений на данные, а индексы предназначены для ускорения доступа к данным. Фактически не имеет значения, как реализована уникальность, но мы, конечно, должны иметь в одиом месте или уникальные индексы или уникальные ограничения. При реализации ограничений есть одао дополнительное преимущество — возможное; ь просматривать ограничения на таблицу, выполняя следующую хранимую процедуру: Ц ap_belpconstraint 'aameTable' Она возвращает набор результатов, сокращенный вариант которого имеет вид: constraintjype constraint_name [...J constraintJceys PRIMARY KEY(clustered) PK_nameTable=1466F737 ... id UNIQUE (non-cliisterea) UQ_nameTable_1S5B1B70 ... flrstName, lastName Хранимая процедура будет использоваться для просмотра всех ограничений, добавляемых к нашим таблицам, в следующих г.тявнх. Обозначение Обозначение ключей ие столь существенно для общей структуры табвцы. Однако все же важно давать ключам некоторые распознаваемые названия для использования в сообщениях об ошибках, SQL Server ие требует от нас давать названия нашим индексам. Если мы не даем названий объявлениям ограничений, как поступали в предыдущих разделах, SQL Server назначает названия аа нас. Например, следующие названия Выли выбраны, когда мы запускали код предыдущего примера: PK_nameTable_1 ЕМ98В2 UQ_nameTable_t FCDBCEB UQ_nameT abte_20C 1Е124 UQ_nameTab!e_21B6055D Они немного говорят читателю. Хотя и можно их рассматривать с помощью программных средств, наподобие Enterprise Manager, вероятно, лучше сделать их как-то более понятными. Стандарт обозначения, который мы будем использовать в втой книге, довольно прост: Щ <тип> <имя_таблицы> <описание> Для первичного ключа тип был бы РК (Primary Key — первичный ключ), а для вторичного ключа мы будем использовать АК (Alternate Key — вторичный ключ), Что касается описания, мы опустили бы его для первичного ключа, потому что у нас может быть только единственный экземпляр первичного ключа. Для описания вторичных ключей используйте или название столбца для единственного столбца, или краткое описание вторичного ключа, включающего больше одного столбца. Так, для нашего примера мы получили бы следующее: I CREATE TABLE nameTable id integer NOT NULL IDENTITY CONSTRAINT РКг.амТаЫв PRIMARY KEY,
IfirstNarae varcher=tl5-J NOT NULL CONSTRAINT ЛКпавжТаЫ«_£1сММя» OHISUE, LastNwe varahai(15f NULL СОИЯИШИИ BKIQtJE, CONSTRAINT AKna.TieTabla_fullName UNIQUE (firstKajae, lastKaael Это дало бы нам следующие названия, делая их более легкими для работы о ограничениями: PKnameTable AKnameTableJuIlName Aknarr cTable JastNam е AknarreTableJirstName Другие индексы О любых индексах, которые мы добавляем к индексам, обеспечивающим уникальность, нужно позаботиться на стадии настройки функционировагния. Это — очень- важный момент, на который я буду обращать внимание много раз. Индексы делают доступ к данным ; некоторым образом быстрее, но они также связаны с издержками. Каждый раз, когда происходят любые изменения в индексируемых столбцах, индекс также должен быть изменен. При единственном изменении это время действительно мало, но чем более активна система, тем больше это затронет функционирование. В явагай OLTP-системе только а очевидных случая* я бы защищал использование "догадок" — добавление индексов прежде, чем возникает потребность — вместо использования "настроек", где мы реагируем на известные проблемы функционирования. Только в этом случае мы будем .способны решить, как "настройка" функционирования затронет остальную часть ВД, Отношения Мы уже рассмотрели в некоторой степени отношения, так что не будем говорить слишком много относительна причин их использования,: В атом разделе мы просто обсудим, как реализовать отношения. Первое, что нужно сделать -— ввести новый оператор. Это оператор ALTER TABLE: Ц ALTER TABLE <ИЫЯта5лици> Оператор ALTER - TABLE позволяет нам изменять и добавлять столбцы, проверять ограничения, а также позволять и запрещать работу триггеров. В этом разделе мы рассмотрим, как добавить ограничения к таблицам. Обрати»» внимание, что вы можете «о сделать с помощью оператора CREATE TABLE. Однако так как мы часто создаем все наши таблицы сразу, лучше использовать команда- ALTER TABLE для уже существующих таблиц, нежели создавать таблицы в порядке, когда таблицы-предки создаются перед зависимыми таблицами-потомками. Внешние ключи Внешний ключ — фактически тот же первичный ключ, мигрирующий в таблицу-потомок, но другой таблицы, которая представляет сущность, из которой он берется. Реализация - внешних ключей — довольно простая задача в SQL Server 2000, Имеется несколько , проблем, с которыми мы сталкиваемся при формировании отношений: Q Каскадированные удаления (если удалена строка предка, то удаляются любые связанные значения потомков, которые обращаются в ключу удаленной строки). □ Отношения, которые охватывают разные ВД.
Основной синтаксис оператора ALTER TABLE для добавления ограничений внешнего ключа довольно прост (команда ALTBR TABLE будет иеполмоватьея неоднократно в этой и последующей главах, чтобы добавить ограничения): I ALTER TABLE <иыя_та6лиаы> №& CONSTRAINT [<имя_ограничения>1 FOREIGN КЕТ REFERENCES <тавлица_для_ссылки> (<стол6цы_для_ССыпки>} Обратите внимание, что тот кт демонстрирует добавление ограничения. Относительно других действий, которые вы можете выполнить, используя ALTER TABLE, ем. документацию SQL Server 2000. □ <имя_таблш»> — таблица-потомок в отношении, О <таблииа_для_ссылки> — таблица-предок в отношении. □ <столбцы_для_аеялки> — разделенный запятыми вписок столбцов в таблице-потомке в том же порядке, что и столбцы в первичном ключе таблицы-предка, Если вам нужно реализовать необязательное отношение (где мигрирующий ключ может принимать вначеяие NULL) подобие следующему: parent СМИ perentlfi: int NOT NULL л childld: int NOT NULL perentAanbcte varchar (Z0) NUU. '' parentld^NUU. chiidAttri&ute: varchar(20; NOT NLIl| - ...то это реализуется почти так же, как в предыдущем случае. Оператор ALTER TABLE — тот же самый, яо вы можете заметить, что столбец child, pa rent Id может принимать значение NULL. Когда ключ ссылки допускает значение NULL, SQL Server знает, что вы хотите, чтобы он был необязательным. Вы не должны иметь NULL в первичном ключе, потому что, как мы выяснили, первичный ключ не может иметь атрибут, допускающий значение NULL. Это столь же просто, как и защитить отношения предок-потомок, которые мы установили в нашем проекте. Вскоре будут представлены примеры отношений и касиалиронанных удалений, * также краткое обсуждение отношений между БД. Обозначение При выборе имен для наших объектов мы обычно создавали имена, которые позволяли пользователю взаимодействовать с объектами. Для ограничений и индексов нам нужны имена, которые указывают на использование объекта. То же самое справедливо и для имен отношений: хотя возможно при некоторых обстоятельствах выбрать одно и то же имя для двух отношений, мы ие сможем это реализовать SQL Ser.'er, Следовательно, мы должны рассмотреть назначение имев для отношений. Стандартные обозначения для отношений,.которые мы будем использовать в этой книге, берут для имени объекта глагол от логической модели и включают имена таблицы-предка и таблицы-потомка, чтобы создать уникальное имя, а также чтобы легче определить любые ограничения, когда просматривается список имен.
Мы используем следующий шаблон для нашего имени: Ц <тавлица-предок>5<глагол>8<та6лица~потомок> Например, рассмотрим в качестве примера пару таблиц. сатрапу Product corr.panyld sells _ produced пате 1~ • гетра л у Id iFK) ~~ —— [пипе Здесь сатрапу — таблица "компания"; companyld — идентификатор компании; пате — имя; sells продает; product — таблица "товар"; productld — идентификатор товара; cotnpanyld — идентификатор компании. Чтобы реализовать это отношение, мы можем определить имя company| seiIsSproduct и так же как и с именами внешних ключей реализовать следующий синтаксис: I ALTER TABLE procfact ADD CONSTRAINT conspanySsallaSproduct FOREIGN KEY REFERENCES parent (companyldj Обратите внимание на использование знака доллара в имени. Вспомнив наши стандарты идентификаторов, которые мы обсуждали ранее в главе, можно прийти к выводу, что нет никаких действительно хороших значений для разграничения или разделения отдельных частей имени, корме знака доллара. Кроме букв и цифр мы можем использовать только три специальных символа: знак доллара, амперсанд и знак подчеркивания. Знак подчеркивания часто используется внутри имен объектов (вместо способа "верблюда", который мы используем). Амперсанд часто используетея для переменных, а отношение таковым ие является. Следовательно, для ясности мы будем использовать знак доллара между именами. Мои имена объектов, по-видимому, будут удивительными для некоторых, не ото лишь мое собственное соглашение относительно имен, и я испальзую знак доллара, чтобы разделять части имени объекта, потому что ото не илиеи» никакого значения для SQL Server, и я могу использовать это соглашение независимо от выбранного стандарта обозначений. Это — другая область, где играют, роль личные вкусы. Например, если мы имеем следующие таблицы в нашей модели: Эа?эп: сЫИ pwertfd: int NOT NULL имеет cnildid:'М NOT NULL < ' MrentAMMerweHftaFPQJ WU. psrentld: WliOT NULL cn^oAttribuie: WOMANS? - можно реализовать, используя следующий фрагмент создания таблиц: CREATE TABLE parent parentld int IDENTITY NOT NULL CONSTRAINT PKparent PRIMARY KEY, Продолжение кода на следующей странице
parentAttribute varchar(20) NULL > CREATE TABLE Chile ( childld int IDENTITv HOT NULL CONSTRAINT FKchild PRIMART KEY, parentld int NOT NULL, ChildAttribute varehar(20) HOT NULL J Мы можем реализовать отношение, используя следующий синтаксис: ALTER TABLE child В ADO CONSTRAINT childShaeSparent I FOREIGN KEV (parentld) REFERENCES parent Если мы создали это ограничение, то можем проверить его, пытаясь ввести данные в таблицу-предок, а затем в тавлиду-потомок: INSERT INTO parent(parentArtribute) VALUES ('parent'} — получение последнего идентифицирующего значения, помещенного в текущий контекст DECLARE eparentld int SELECT Bparentld - scopa_id.entitу (1 INSERT INTO child (parentld, childAttribute) VALUES (gparentld, 'child'} DELETE FROM parent Это вызывает следующую ошибку в ВД при выполнении: Server: Msg 547, Level 16, State 1, Une 1 DELETE statement conflicted with COLUMN REFERENCE constraint ’chWShasSparent’. The conflict occurred in database 'tempefc', table 'chid', column ’parentld'. (Сервер: сообщение 547, уровень 16, состояние 1, строка 1 Оператор DELETE конфликтует с ограничением COLUMN REFERENCE (ссылка на столбец) 'child$has$parent'. Конфликт возник в БД ’tampdb', таблица 'child, столбец ’parentld.) Каскадирование удалений и обновлений В нашем предыдущем примере БД предотвращала нас от удаления записи в таблице-предке, если существовала вались в таблице-потомке, имеющая отношение к таблице-предку как к атрибуту, В большинстве случаев это хорошо, во имеются случаи, когда данные в таблице-потомке так объединены е данными таблицы-предка, что когда запись родительской таблицы изменена или удалена, SQL Server будет всегда стремиться изменить или удалить запись в таблице-потомке без какого-либо дальнейшего взаимодействия с пользователем,
Рассмотрим случай таблицы товаров и таблицы, которая хранит описательную спецификацию: - - prodaBtSpecgicaton Здесь product — таблица "товар”; produdld — идентификатор товара; паяв — имя; productSpecification — таблица "спецификация товара"; productSpecifLcationld — идентификатор спецификации товара; value — величина. В этом случае, если вы мы хотели удалить запись из таблицы товаров, маловероятно, что мы хотели вы сохранить соответствующую запись в «6ximeprcductSpecifiaation. Спосовность выполнять автоматически некоторое неявное изменение-В таблице-потомке, как результат явного действия в таблице-предке, известна как каскадирование изменений. Для удаления вы можете установить ограничение, которое автоматически удалит потомков отношения при некоторых действиях на таблице-предке. В случае обновления, если вы изменяете значение первичного ключа таблицы-предка, то изменение. каскадировало вы значение внешнего ключа таблицы-потомка.. Имеется другое менее используемое действие каскадирования, которое SQL Server фактически не осуществляет — тип каскадируемых действий, когда запись-из таблицы-потомка полностью не удаляется, но ее ключевая ссылка устанавливается в NULL. Это может быть реализовано только на необязательных отношениях, так как если внешний ключ не допускай значение KULL, данную операцию выполнить не удастся Обсуждение того, как* осуществить эту операцию, будет проведено в следующей главе, поскольку она реализуется е помощью триггеров. Две наиболее захватывающие новые особенности SQL Server 2000 — это возможность иметь каскадированное удаление в объявленной ссылочной целостности и каскадированное обновление. В предыдущих версиях SQL Server операция каскадирования требовала триггера. Это было неприятно, потому что мы не могли при этом использовать отношения с помощью внешнего ключа и средства, которые могли бы использовать объявленные отношения. Синтаксис для осуществления операций нижадированин прост. Мы просто добавляем ОН DELETE CASCADE или ON UPDATE CASCADE к нашему объявлению ограничения: I ALTER TABLE <имз_табл:":ы> - - - ADD . n -АХЯТ <имя_о₽раничения> - г - - FOREIGN KEY REFERENCES <табйица_для_ссылки> 1<столвш»_для_ссьо:ки> 1 . . ГОК DELETE <CASCADE> [ <N0 ACTIONS-j £OH UEDATE <CASCAC-E> | <N0 ACTIONS-] Использование опции NO ACTION для любого объявления устанавливает отношения в "нормальимй" запрещающий случай, когда в случае наличия записи-потомка и изменения ключа или удаления, выполняемых е зяниаыо предка, возникает ошибка.
Может показаться странным так использовать NO ACTION, ио что делать, если операция удаления не удалась, — мы просто берем NO ACTION и завершаем операцию, Для продолжения нашего примера с ограничением crilciShasSparent отметим» что после создания записи-потомке мы не могли удалять исходную запись-предка. Модифицируем этот пример, чтобы позволить каскадированные удаления. — замепш, что здесь нет команда constraint в операторе alter ALTER TABLE child DROR CONSTRAINT childShaaSparent ALTER TABLE child ADD CONSTRAINT childShasSparent FOREIGN КЕУ (parentldj REFERENCES parent ON DELETE CASCADE SELECT » FROM parent SELECT • FROM child Этот запрос возвращает parentld parentAttribute 2 parent childld parentld childAtttibute 2 2 child Далее запустим удаление и снова выполним операции SELECT: I DELETE from parent SELECT * FROM parent SELECT " FROM child что возвратит: parentld parentAttribute childld parentld childAttribute Просто реализовать таким же способом и пример каскадирования г>пнон.тения. Обратите внимание, однако, что первичный ключ не может выть реализовав как столбец идентификации, чтобы обеепечить каскадирование обновления, так как идентифицирующие значения не могут быть изменены. Взаимные отношения БД Основная трудность с внешними ключами, основанными на ограничениях — то, что таблицы, участвующие в отношениях, не могут охватывать различные ВД. Когда возникает эта ситуация, мы должны реаливовать наши отношения через триггеры.
Вообще-то плохо проектировать БД с взаимными отношениями между ними. БД должна рассматриваться как совокупность связанных таблиц, которые всегда синхронизированы. Когда мы проектируем ВД, которые распространяются на различные БД иди даже серверы, мы распространяем ссылки на данные, находящиеся не в пределах БД, ж не можем гарантировать их существование. Однако бывают случаи, когда взаимные отношения БД неизбежны. Распределение деталей вашей БД между разработчиками На стадии логического моделирования проекта мы потратили много еда», вводя определения наших таблиц, столбцов и других объектов. В зтом разделе мы рассмотрим некоторые средства, которые должны дать нашим клиентам понимание того, тот мы создали для них. Эти средства обеспечат отчеты по всем объектам БД м их свойствам, и реализованным, и информационным. Однако построение этих отчетоз, поддержание их в актуальном состоянии и распределение их мажет быть серьезной задачей. На протяжении остальной части книги мы собираемся лишь исследовать средства, которые SQL Server 2000 предоставляет аам. Имеется несколько методов, помещенных в SQL Server: О информационная схема и системные хранимые процедуры; □ новые наглядные средства; □ сервисы метаданных. Мы детально рассмотрим первые два метода из этого списка, но опустим сервисы метаданных. Они очей, ценны, но их непросто использовать. Эти сервисы, прежде всего, используются для обмена метаданными между средствами моделирования — DTS и OLAP и не совсем уместны с точки зрения тематики этой книги. Если вы интересуетесь, то можете выяснить детали относите»» сервисов метаданных (Meta Data Services) н SQL Server 2000 Books Online. Информационная схема и системные хранимые процедуры Чтобы пользователям можяо выло просто рассматривать структуры ваших таблиц, SQL Server предлагает нам информационную схему (Information Schema) и системные хранимые процедуры (system stored procedures). Информационная схема >— набор двадцати представлений, основанных на стандартном (ANSI SQL-92) определения механизма формирования стандартного набора метатаблиц, обеспечивающего разработку с использованием нескольких платформ. Они определяют многие из объектов SQL Server — все, которые понятны для стандарта SQL-92 — и обеспечивают быстрый доступ к структурам наших таблиц. В этих представлениях мы используем несколько отличающийся, но достаточно понятный набор семантических представлений для общих объектов: Название в SQL Server Название в информационной гуемь (SQL-92) database (ВД) Catalog (каталог) owner (владелец) - - Schema (схема) user-defined data type (тип данных, определяемый пользователем) Domain (домен)
Следующая таблица jaei список процедур метаданных и соответствующих представлений инфофмационной схемы (INFORMATIQK_SCfffiMA). Объяснение, которое дается в третьем столбце таблицы, — просто краткое примечание, для чего аепольяуютея различные процедуры и представления. Для любого программиста ВД желательно хорошо понимать, как использовать эти объекты, чтобы запросить метаданные модели. Лучший способ для этого — посмотреть полную информацию в SQL Server 2000 Books Online. Таким образом, SQL Server предлагает два совершенно разных способа просмотра -тех же самых системных данных. Сравнивая два различных метода, можно выделить два момента, которые важны для меня. Системные хранимые процедура — аява ориентируемы на реализацию в SQL Server. Представления информационной схемы дают разработанный общими усилиями взгляд, который может подойти не для каждого. Одиако определения, которые они дают, хотя и еиецифичеекие, ио все-таки очень полезны. По моему мнению, лучше использовать представления информационной схемы всегда, когда, это возможно, потому что они основаны на стандарте SQL 92 н будут, вероятно, улучшены в будущем. Кроме того, так как каждая ВД, созданная на основе SQL 98, будет в конечном счете иметь идентичный набор реализованных представлений, использование этих представлений должно облегчить пониманиедругих систем БД. Системная хранимая процедура Соответствующее представление информационной схемы Объяснение sp_ s е rve г_infо (информация сервера) отсутствует Дает список настроек и информацию, связанную с сервером. Информация о сервере же может быть помещена в информационную схему. sp_databases (базы данных) SCHEMATA (схемы) Оба дают список ВД и серверов. Список представления SCHEMATA содержит более полезную и^ортцию Р создателе ВД, в то время как sp_dataaases дает разм еры ВД. sp_tables (таблицы) TABLES, VIEWS Дяет список таблиц и : « : : А: : : : ; : : : Ф : ГТ : (таблицы, представления) предтчнипстпгй всистеме. - sparables содержит гиит-мы?! табл ип ы, •rnfimnjtj пользователя и щждставления. Как ни странно, TABLES содержит перечень таблиц и гфедсгавлеши!, a views — только представления. Оно содержит расширенную иифоримдайдиносительно представлений, такую как обновлнемостъ, определения, контролируете» параметры и некоторые другие характеристики.
Системная хранимая процедура Соответствующе е представлен и е и нформ а 1 (ион но й схе мы Объяснение ар_С01итП8 (столбцы) COLUMNS (столбцы) В то время как процедура sp_col unias требует параметра, характеризующего таблицу, для которой она испбпыувтсяу представление COLUMNS этого же требует; оба содержат полезную и интересную информацию относительно столбцов. sp_stored_procedures (хранимые процедуры) ROUTINES (подпрограммы) Оба перечисляют хранимые процедуры и функции, ио sp_stored_procedures имеет несколько столбцов, которые еще не были реализованы, a ROUTINES — все. Ова этих метода имеют подсказку, как войти в SQL и SQL Server. ap_SprOC_CQlUBnS (параметры хранимых процедур) PARAMETERS (параметры) Оба перечисляют параметры хранимых процедур и функции. Отсутствует ROUTINE_COLUMNS (столбцы подпрограмм) Перечисляет столбцы таблиц, возвращаемых функциями, конечно, когда они возвращают таблицы. 5p_helptext (справочный текст) VIEWS, ROUTINES (представления, подпрограммы) sp_helptext — одна из тех процедур, которая должна быть понятна любому администратору. Используя эту процедуру, вы можете получать текст любой процедуры, значения по умолчанию, расширенной хранимой процедуры (которая возвращает используемую DLL) или функции, находящейся в системе. VIEWS и ROUTINES похожи, поскольку они также возвратят характеристики представления или подпрограммы, наподобие справочного текста. Продолжение тавлицы на сле&ующеР странице Ж
Системная хранимая процедура Соответствующее представление информационной схемы Объяснение sp_colun>:':_privileqes (права доступа к столбцам) COWMN_₽RIVILEGES (права доступа к столбцам) Оба перечисляют права доступа к столбцам таблицы. sp_spec i al_columas (специальные столбцы) Отсутствует Возвращает первичный ключ таблицы. sp_statisties (статистика) Отсутствует sp_statisties отображает информацию об индексах таблицы. В информационной схеме нет информации об индексах. sp_:keys (внешние ключи) TABbE_CONSTFAINTS, COMSTRAINT_TABLE_USAGE, REFERENTIAL_COKSTRAINTS, KSY_COWMN_USAGE (ограничения таблицы, использование ограничений таблицы, ссылочные ограничения, использование ключевых столбцов) sp_f keys дает список ограничений за счет внешних ключей таблицы. TABLB_CONSTRAIKTS и CONSTRAIST_TABLE_OSAGE перечисляют все ограиячеиия таблицы, первое — возможные, а второе — действительные, REFEREHTIAL_CONSTRAINTS перечисляет все агряттчетгия ВД за счет внешних ключей. KEY_COLUMN_USAGE перечисляет столбцы в первичном и внешних ключах, обеспечивающих ограничения. sp_pkeys (первичные ключи) KEY_COLUMN_US--- TABLE_CONSTRAINTS (использование ключевых столбцов, ограничения таблицы) sp_pkeys перечисляет первичные ключи таблицы. KEY_COLU”N_USAGE и TABLE_CCNSTRAINTS уже упоминались в предыдущем разделе sp_f keys. sp_helpconstraint (справка об ограничениях) REFERENTIAL_CORSTRAINTS, CHECK_CONSTRAINTS, TABLE_C0NSTRAINT9, CON8TRAINT_COWMN_USAGE, CONSTRAINT_TABLE_USAGE (ссылочные ограиячеиия, проверка ограничений, ограничения таблиц, использование ограничений столбцов, использование ограничений таблиц) sp_helpconstraint дает перечень ограничений таблицы, Все элементы информационной схемы уже выли обсуждены.
Системная хранимая процедура Соответствующее представление информационной схемы Объяснение sp_table_privileges TABLE_₽RIVILEGES (права sp_table^privlleges дает (права доступа к таблицам) доступа к таблицам) список прав доступа для данной «блицы, которые можно предоставлять. TABEE__FRTtIbEGES ' отображая1 по одной строке на каждое право доступа, предоставленное ; пользователю. - sp_cable_pr1V1leges и 3"ABLE_₽RIVI£B<5ES связаны, но — фактически несут разную информацию. sp_-.abie_pri'.': leges говорит а том* что можно предоставлять, в то время как TABLE_₽MVI LEGES связано с тем, что было . предоставлено. sp_datatype_info (информация о типах данных) DOMAINS (домены) sp_da tatype_in £о перечисляет все типы данных, которые и сп ол ьзу ются в системе и ее свойствах, в то время как DCMAINS отображает только типы данных, определяемые пользователем. Отсутствует DOMMH_COWSTRAINT-S, COLUMN_DOMA:lN_USAGE (ограничения доменов, использование доменов столбцов) Два дополнительных представления добавлены для доменов и могут быть действительно удобны. DOMMS_COTSTRAI«S перечисляет все ограничения (правила), которые были добавлены к домену. COLuM’I_DOMAINJJSAGE перечисляет все таблицы, где использовался домен. Имеется некоторая информация а этой группе функций, которую жы не обсудили. в первую очередь связанная с функциями, обеспечивающими права доступа и которые мы будем рассматривать в главе 12.
Оставим читателю разобраться, как реализовать выполнение процедур, просмотр таблиц и изучение SQL Server 2000 Books Online, раздел System Stored Procedure (Системные хранимые процедуры) (особенно catalog procedures — процедуры каталога, но лучше изучить все), а также раздел Information Schema Views (Представления информационной схемы). С комбинацией процедур каталога, объектов информааиовяой схемы и функций Т-SQL мы можем просмотреть практически любую часть метаданных, содержащихся в ВД, которую создала. Новые описательные свойства Во время нашего моделирования мы создали описания, записи и различные наборы данных, которые являются чрезвычайно полезными для разработчика, чтобы понять все "почему" и "для чего" использования таблиц, которые мы создали. В предыдущих версиях SQL Server было очень трудно фактически использовать эти данные, В SQL Server 2000 фирма Microsoft ввела расширенные свойства, которые позволяют вам размещать определенную информацию об объектах. Это действительно хорошо, потому что позволяет нам расширить метаданные наших таблиц способами, которые могут использоваться нашими приложениями с помощью простых SQL-операторов. Создавая эти свойства, мы жожем формировать хранилище информации, которое прикладные разработчики могут использовать для следующих целей: □ чтобы понять, какие данные используются в столбцах; □ чтобы разместить информацию, используемую в приложениях, яаиример: □ заголовки, чтобы показать их форму при отображения столбцов; О сообщения ов ошибках, выводимые при нарушении ограничений; О правила форматирован ия для отображения вводимых данных. Расширенные свойства размешаются как столбцы Timasql_variant, так что они могут содержать любые типы данных кроме полей типа text или image. С целью подключения расширенных свойств объекты в SQL Server классифицируются на три уровня, называемые схемой. Следующее дерево иллюстрирует эту иерархию: Уровень2 Column (Столбец) Index { > и де <с i Ceneirairt (ограничение) Trigger (триггер) Column (столбец) Лаем (Инда®—талы® си«- ев»н»*еприжт®)»ния) Тпяввгрржтар — тапы® INSTEAD OFj Peremeter (параметр} Column {сталвец} Constraint (ограниченна) ParamBter (параметр)
Обратите внимание; в этой диаграмме мы упомянули новый термин: схем но-связанный. Связывание схемы означает механизм, который является новым в SQL Server 2000: когда вы воздаете схемно-связанное представление или схемно-связываюшую функцию, определяемую пользователем, то можете проинструктировать SQL Server нс допускать никаких изменений основных таблиц, которые оно (представление) или она (функция) использует. Мы рассмотрим это более детально в следующей главе. Только для этих объектов мы можем определять расширевиые свойства, поскольку SQL Server проверят имена, которые мы используем для этих свойств. Схема присвоения имен довольно проста. Мы можем добавить свойство пользователю, что не требует никакой другой информации, но чтобы добавить свойство к таблице, мы сначала должны иметь ссылку на пользователя, который владеет таблицей, Чтобы добавить свойство к столбу, мы должны знать пользователя, который владеет таблицей, а также имя-таблицы в имя столбца. Чтобы обслуживать расширенные свойства, мы имеем следующие функции и хранимые процедуры: □ sp_addextendedproperty — используется для добавления нового расширенного свойства; О sp_dropextendedproperty -— используется для удаления существующего расширенного свойства; □ sp_updateextendedproperty — используется для корректировки существующего расширенного свойства; . ' " □ f istexte-dedproperty — системная функция, которая может использоватыл, чтобы сформировать список расширенным свойств. - - ' ' В следующем примере мн рассмотрим конкретный синтаксис каждой команды, когда будем использовать ее, но важно понять основной путь, в соответствии с которым они работают. Каждая имеет следующие параметры: □ в name — имя определяемого пользователем свойства; □ lvalue — какое задать значение при создании или изменении евоиетиг □ SlevelOtype — или тип пользователя, или определяемый пользователем тип данных; □ glevelOname — имя объекта того типа, который определен параметром 01evelOtype; □ glevelltype — имя типа объекта, который находится на 1-м уровне ветви дерев непосредственно под параметром user, Tima Table, View и t. д.; -= : :щ - = =ж
□ Blevellname — название объекта, имеющего тип, который задан в параметре вlevel!type; □ flevel2type — имя типа объекта, который находится на 2-м уровне ветви дерева под значением, заданным параметром fie veil type; например, еелн Sieve11type — Table, то #level2type может быть Column, Index, Constraint или Trigger; □ Blevelzname тая объекта, имеющего тип, который задан в параметре eievelZtype. Например, пусть в следующем фрагменте мы создаем таблицу, которую назовем person (человек); CREATE TABLE person ( personld int HOT NULL IDENTITY, firstMame varchar (40> KOT HULL, lastName varchar(40) NOT SULL( socialSecurityNwnber varchar(10) NOT NULL Чтобы сделать это, мы собираемся добавить к таблице и столбцам свойство по имени MS_DESCRIPTTON. Почему MS_DESCRI₽TION? Потому что это — имя, которое выбрано фирмой Microsoft, чтобы использовать в Enterprise Manager для размещения описаний. Обратите внимание, что ото имя может меняться. Я нашел имя свойства, используя SQL Profiler, чтобы проследить, как SQL Server сохранил свойство. а затем сформировал свою собственную версию сценария. Профилирование таким же образам, как ото делает Enterprise. Manager, является, быстрым способом определить, как описать задачи, которые, казалось бы, возможно оделять, используя только средства графического пользовательского интерфейса (GUI — Graphical User Interface), Итак, мы выполняем следующий фрагмент после создания таблицы; — GUI-описаяив таблицы dbo.person (табиша parson владельца dbo> EXEC sp_adaaxtendedproperty 9name - ,MS_Descriptioa', 9value - 'Пример расширенных сзойств в таблице person', SlevelOtype = 'User*, SlevelCname - 'dbo', ilevel1type « 'table', llevellnanie = 'person' — GUI-олисание dbo.pereon.personld (атрибут personld — таблицы person владельца dboj EXEC sp_addexandedproperty Snare = 'MS_Description' , lvalue • 'указатель первичного ключа ва экземпляр записи а таб-ице person', eieveiOtype = 'Oser', @levelOna“e « 'dbo', eievelltype - 'table', eievellname • 'person', eievelZtype = 'column', SLevelZname = 'personld' — GUI-описание dbo.person.firstName (атрибут firstName
— таВдаиш person владельца dbo) EXEC sp_addexende4>roperty Sname - 'MS_Description', evalue - 'Имя человека', glevelOtype'User* t @levelОгате - 'ilx', - glevelltype - 'tabi**, eievellnane « ''регвоп',. Ilevelltype = ' coluen', Ibewl2name - 'firstName' — GUi-onwcaiwe dbo.person.lastSsse (атрибут lastHam — табдацы person владельца dboj EXEC sp_addexendedproperty fname - 'Ma_Description’, lvalue - 'фамилия человека', flevelOtype - 'User*, eievelOnaiae - 'dbo', gievelltype - ' table' , tlevellnaiae “ 'person', glevelZtype - 'colwan', fbevelZnawe = 'lastNawe’ — GUi-описание dbo.person.socialSeauritySesber (атрибут — socialSecuxityHwaher таблицы person владельца dbo) EXEC sp_addexendedproperty впаже - ' MS_De scripts-on', lvalue - 'описавив столбца с номерами социального обеспечения', llevelOtype - 'User*, filevalOnaae - 'dbo', gievelltype - 'table*, glevellnam — 'pereon', 01evel2type - 'coluum*, flevel2na®« = 'aocialSecurityHueber' Теперь войдем в Enterprise Manager, щелкнув правой клавишей мыши на нашей таблице и выбрав Design Table (схема таблицы); мы увидим наше описание следующим образом:
А взглянув на свойства таблицы (щелкнув иконку справа от Save — сохранить) мы заметим, что теперь имеем описание; Pwpeifes Очевидно, это не совеем то, что мы обычно делаем, но если мы и?<;ес»усовершенствованные возможности в наших средствах моделирования данных, то должны быть способны выводить эти данные, используя Er.ierprise Manager, который намного более полезен как средство для наших программистов. Чтобы изменить свойство, мы могла бы использовать следующий тип сценария: -- смотрим, с : sye : ли свойство IF EXISTS (SELF';: ’ FROM ::FK_LI3'»Xma3EO₽RO₽ERT¥«'MS_DescriptlMl*< 'User*, 'dbo'r 'table*, *person', 'column', * BecielSecurityHunfoer’> ) BEGIN 1 -- если -СЙСТ50 уже существует exec sp_update«ctendedproperty gname - 'MS_Description', а-.--..--? ~ ' гоаударетаенний идентификациоиньй иемер человека', 9level0type ~ 'Oser', (levelOnaaie - 'dbo', glevelltype - 'cable', glevellti&me - 'реикж', glevelltype - 'column', tleveliname - ' soeielSecuieltyMuabei'' EHD ELSE BEGIN -- в противнем случае создать его EXEC sp_addexendedproperty gname « ' MS_Deacnption', (lvalue - 'государственный идентификационный номер человека*.
IeievelOtype * 'User*, 91evel0name ” 'dbo', flevelltype ~ 'table', glevellnarne “ 'person', fl lave 12 type = ’coll»', flev-elZname ' socialSecuritytftjmber' END Объект, fn_listExtendedProperty (список расширенных свойств) является системной функцией, которую мы рассмотрим позже в главе 12. Наше последнее дополнение к атому примеру должно добавить расширенное свойство к таблице, которое процессы пользователя могут использовать при выполнении программы. Для этого мы добавим свойство, представляющее собой маску для ввода в таблицу, чтобы мы могли создать общий метод для ввода данных в столбец, IEXEC sp_addexendedproparty впате “ 'Маска ввода’, gvalue - ' ч*-**-****> г aievelOtype = 'User', glevelOtuusa • *dbo*, eievelltype - 'table*, eievellnaie* = 'parson', 31evel2type - 'column*, 91evel2name — ‘socialSecurityNunber' Заметьте, что мы можем создавать любое расширенное свойство, которое пожелаем. Названия должны вписаться в тип данных sysnaee (системное имя), и могут включать пробелы, неалфавитно-адфровые символы или даже двоичные значения. Эта новая особенность документации имеет некоторые интересные возможности для создания приложений и использования большей части метаданных. которые мы хранили в различных местах, даже не используя особенно важвую документацию. Дополнительную информацию можно получить в SQL Server 2000 Books Online, раздел Using Extended Properties on Database Objects (Использование расширенных свойств в объектах бая данных). Учебный пример Возвращаясь к нашему учебному примеру, мы теперь должны заняться созданием физических структур, чтобы воплотить теорию в практику. Вудем следовать нашей протоптанной дорожке, когда мы определяем ситуации, которые могут быть слишком трудными для реализации, и устраняем их, затем выбираем первичные ключи и типы данных и, наконец, представляем физическую модель, которую мы создадим, Вудем использовать макроязык Т-SQL для реализации нашей ВД, включая доваалеяие описательной информации из нашего исходного анализа. Очистка модели Первым шагом должна быть корректировка любых мест в модели, которые яв,’лкяся ашшшм трудными для реализации, В некоторых случаях нам придется опустись большие области реализации, моторы© мы хотели бы выполнять, естествен», получая при этом не лучшее решение. В зависимости от ситуации, средств или даже общих стандартов нам, вероятно, придется изменять пути реализации отдельных частей, хотя мы должны, *. конце концов, получить решение, которое является фунитгис-няльяо тишимым логической модели, -Каждое црос-бразозание, которое мы делаем в модели во ърша стадии физического моделирования, не должно изменить набор данных, которые размещаются в нашем ВД, если только мы не обнаружим в дальнейшем атрибутов, которые следует добавить к логической модели. 12 1868 335
Подтипы Одна из наиболее трудных ситуаций для реализации в SQL Server — это наличие подтипов, В логической модели (которую мы закончили создавать в конце главы 8) имеется пример как раз такого случая: подтип сделки. Мы моделировали его как полный подтип, то есть, все возможные случаи были представлены в диаграмме. (Обозначения см. в главах 5 и 7.) Однако мы не определили никаких дополнительных атрибутов для сущностей deposit (депозит) и directWithdrawal (прямое изъятие), чтобы добавить их к модели. В этом случае, мы могли просто заменить наш подтип на неполный вариант (где не всякое значение подтипа фактически реализовано таблицей) и удалить все, что связано с понятиями "депозит" и "прямое изъятие". В зависимости от того, насколько велик подтип (один или два атрибута или больше плюс связанные таблицы), мы можем пожелать назначить эти атрибуты предку отношения. Именно это мы сделаем в нашем примере-. Основам причина здесь — практичность, так как каждый иа подтипов очень близок друг к другу. Позже, мы должны будем написать некоторый специальный код, чтобы защитить данные от неправильного ввода в таблицы подтипа. Основываясь на значении дискриминатора, в подтипе довольно очевидно, какие поля действительно имеют силу (в этом случае дискриминатор — тип сделки, потому что он определяет, в какой таблице-потомке мы должны искать значения подтипа), но как только мы берем вое атрибуты всех сущностей подтипа и возвращаем их предку, это может стать менее очевидным. В нашем случае мы имеем три атрибута, которые можно использовать:
□ payee (получатель платежа) — чеки всегда имеют получателей платежа. Прямые изъятия могут их иметь, но деповиты никогда не имеют получателей платежа. Мы могли бы увеличить наши таблицы, чтобы разместить информацию о том, откуда поступают деньги на депозит, но не будем делать этого, потому что хотим ограничить размер проекта и потому что такое изменение нарушило вы планы реализации, которые были предварительно одобрены клиентом, □ signature (подпись) — только чеки имеют подписи. Этот атрибут кажется несколько бессмысленным, но мы оставим его как пример. Вели бы мы ату систему реализовывали полностью, данный атрибут, вероятно, соответствовал бы большому дереву доменов и принадлежал бы депозитам, прямым изъятиям и чекам. Однако мы будем использовать логику предыдущего пункта и скажем, что, поскольку клиент не просил об этом, мы не будем делать это. Мы также изменим эту величину до строки, потому что будем только хранить полное имя человека, который подписал чек, а не изображение подписи. Конечно, если бы anta выла реальная система, мы, видима, спросим бы, клиента, являются ли алш корректировки приемлемыми и, может быть, приняли, вы другое решение. □ checkUsageType (тип использования чека) — атрибут довольно прост; мы изменим его на Transactionallocaticnlype (тип размещения сделки), где пользователь может установить использование/размещение для каждого типа сделки. Итак, мы реконструируем таблицы следующим образом: Здесь дополнительно transactionAllocation — таблица “размещение сделки", transaetionAllooatianTypaM — идентификатор типа размещения сделки, Ьлпкта — идентификатор банка, accounts >— идентификатор счета, transactions — идентификатор сделки, allocationAmount — сумма размещения.
Нужно отметить следующий момент. Таблица сделок не может выть названа transact ion (сделка), потому что это зарезервированное слово, и SQL Server не позволяет использовать его в кода. В английском языке transaction — и банковская сделка, и трансакция (групповая операция), Прим, перев. Мы имеем несколько вариантов, как поступить в этом случае: □ дать таблице другое имя, например, xaction (х-операция); □ всегда обращаться к таблице с названием в скобках: [transaction]. Ни один из них не очень хорош, и transaction (сделка) выло бы лучшим именем для таблицы и обращающихся к ней таблиц. Мы выберем имя transaction и использование скобок при обращении к таблице. Это сохранит имя таблицы более понятным для клиента. Однако будем ссылаться на таблицу с помощью [transaction] только тогда, когда используем это в коде. Посмотрим, как реализовать конкретные правила формирования данных, которые мы будем использовать в следующей главе. Упрощение сложных отношений В некоторых случаях мы будем моделировать объекты, выбранные на стадии логического проектирования, которые являются несколько сложными для практической реализации. В нашей логической модели мы имеем одну ситуацию, которую могли бы посчитать слишком трудной для реализации в гаком виде, как мы ее разработали. Чтобы иметь уникальный почтовый адрес, мы должны просмотреть две таблицы. В нашей модели таблицы address (адрес) и addressLine (строка адреса) имеют вид: address address-ice addree»№ PrimaryKey ^address id; PrimaryKey (FK) add ressUneid: Primary Key cltyfd: PnmarjKey(FK) stated; =dTiarytey (FKi zipCodeld: P'insyKey(FKi имэештелнительнус информацию a addressLine: AddressOescriptiye Line xsonurrena sonuraenc j Они учитывают адрес неограниченного размера и структуры, что часто не является необходимым для физической реализации. Так что мы просто реорганизуем таблицу address следующим образом: address address id; PrimaryKey acWessUne: AddressDesaiptweUne (AK1.1) C'tyid: P-imaryrey (FK) (AKI .2) staiett PrimaryKey (FK) (AKI .3) zlpCateM: Primary Key (FK) (AM. 4) Поле addressLine (строка адреса) используется и для названия улицы, и почтового ящика, и т. д„ размер которого мы определим, когда выберем тины данных.
Таблицы доменов В таблице transaction (сделка) мы имеет столбец type (тип), чтобы задать тип сделки, transaction _____________ fbankld: PrimaryKey (FK) ассОиМИ: PrimaryKey (FK) tranwetonftJ; Primai yKey date; Date number: Number de see peon Desciipntjn amount Amount type: String signature; String payeeid: PrrmaryKey (FK) Обычно мы не будем использовать поле ввода данных в свободной форме, когда нам нужно группировать поля, или, как в случае столбца type таблицы transaction, где мы должны будем, вероятно, использовать значение домена для специальной обработки в нашем пользовательском интерфейсе. Мы хотели вы даже добавить характерную для реализации информацию к нашей таблице, содержащей тип адреса, наподобие расположения имен файлов е иконками, так, чтобы пользовательский интерфейс мог выполнить некоторую проверку типа чека, кредитной карточки при прямом изъятии и т. д. В нашей таблицеtransactionType (тип сделка) мы добавляем столбцы паже (имя) и description (описание). Таким образом мы можем включить большее количество информации, чтобы объяснить, что каждое значение в таблице означает. Снова каждый из этих атрибутов следовало бы показать клиенту и, возможно, добавить к логической модели. Мы видим ии иаменения плюс три других поля логического типа в следующей диаграмме: transaction_________________________ bankid- Pdma rytey (FK) (AKI. 1) accounUd: Primary Key (FK i (AK1 Л) i ran sactionci: Primary Key date: Date number: AiphaNumericNumber (M1.3) desori prion: Description amount: .Amount signature: String - payeeid: PrimaryKey s FK) usertfc Primary Key (Rg statementld: PrimsryKey (FK) .statement I temio: Primar yKey (FK) класснойшpysr transactor.Typeid Pri-naiyhey name: Name description: Description requiresSignatureiFi: Fl requires Payee Fl: Ft allows PayceFI: H Здесь в дополнение к предыдущим обозначениям -transactionType — таблица “тип сделки", transactionTypeld — идентификатор типа сделки, паае — название (сделки), description = описание, requiresSignatureFl — флаг неовходимаети подписи, requiresPayeeFl — флаг необходимости иметь получателя платежа, allowsPayeeFl — флаг возможности иметь получателя платежа. Теперь мы можем писать код, который вынуждает заполнять поле signature (подпись) для некоторого типа сделки и может определить также, обязателен ли получатель платежа.
Построение пользовательского интерфейса будет более легким, петому что мы .имеем данные, которые сообщат нам, когда требуется значение, и изменение требований не будет приводить к написанию дополнительного кода. Каждое из этих полей флагов, вероятно, лучше рассматривать как логические поля, потому что они поддерживают реализацию информации уже на текущей стадии проектирования. Мы будем также использовать эту таблицу как наш тип сделок для таблицы statement-item (элемент отчета) (хотя в этом случае мы не будем использовать поля флагов). Так как наши типы сделок будут характерны для банка, это оградит нас от наличия двойных данных в таблице, плюс это будет обеспечивать соответствие элементов во время балансирования счета намного более простым, но мы не будем, конечно, требовать, чтобы типы сделок совпадали. Удаление ненужных сущностей После дальнейшего просмотра мы замечаем, что таблицаcheckRegister на самом деле не будет реализована как таблица вообще, так что будет только электронный регистр чека, как только будет создана БД. Поэтому мы удалии ее из нашей физической модели, хотя она все еще имеет значение в логической модели, поскольку это — фактическая сущность, с которой нам, вероятно, придется иметь дело. ; eheckRegister_______________ ; сйеЛЛфавгИ: PrimaryKey regsierTjpa: «unknown» Здесь checkRegister — сущность "Проверка регистра"; chcekReglsterld — идентификатор проверки регистра; registerType — тиа регистра. Коррекция ошибок в модели Когда мы выполняем этот процесс, то найдем ошибки в модели, даже если мы используем лучшие средства моделирования и работаем не спеша. Например, в исходной модели мы имели следующие отношения:
Здесь ошибка в отношении между таблицами bank (банк) и statement (отче*): "банки досылают отчеты". Отношение должно выть "банки, посылают отчеты для счетов" и поэтому при реализации отношение между bank и statement должно быть заменено отношением между account (счет) и statement. Мы перемещаем столбец per iodEndtaте (период до последней даты) в таблицу statement, поскольку это будет часть отчета, а не значение, которое мы размещаем, когда отмечаем, что отчет выверен. Мы также опустим отношение между таблицами account и accountReconcile (согласование счета), так как. будем отмечать отчет как выверенный. вв«* совпадает о данными записей в классифицирует staleraentTypelo: РгапауКеу nenerNsn» descriptor Descriptor BaMWtt:);..; .; . . . . . . . stator Mid РпмиуШу ' bankjd PomaryKey ЦК) 1ЛК12) accourtB: PrimaryKey 1FK; з'а'етацТурвга: ргьта-уКау (FK; pre rscsBaiarceDste: Date сшплМШмжМшвй statsmtoOa-a- Qate(AKtl} MteMS-ta&rt WaiCrWteAmtni используется для oooecoeewa сч»та споаощ.* ________ ЬапИа; tWnaryray (Htj accountic Ptoaryfey ЦК) «Мигели: Hteaf yW{FK) par©CEndOate: Date Один из интересных моментов этой модели заключается в том, что для выяснения, когда счет был последний раз выверен, мы должны пройти через таблицу statement к таблице accauntReconcile. Хотя это и может казаться странным ней освященным, в программирование ВД а использованием нормализации, однако это .является одашм из контрольных признаков хорошо разработанной БД. В большей системе вам пришлось бы пройти пять, десять или двадцать таблиц, чтобы выяснить информацию,.подобную этой. Принцип наличия одной части данных, размещенной в одном месте, может делать ВД более сложными для прослеживания данных, но, с другой стороны, устранит избыточные данные и аномалии корректировки — если номер счета размещен более чем в одном месте, и потребуется его изменить, то изменение должно быть сделано в каждом месте, иначе ваши данные станут противоречивыми. Как мы увидим в следующей главе, чтобы ие было возможности получения случайного беспорядка наших данных, придется выполнить дополнительное внутреннее кодирование. Вы не должны рассматривать обнаружение ошибок, наподобие этой, в вашей модели как неудачу. Очень часто мы будем пропускать материал, когда выполняем стадию проектирования. Часто наши клиенты будут изменять свои требования к БД, с которыми мн должны иметь дело. Ошибки не могут выть исключены, поскольку каждый архитектор ВД — человек. Часто они незаметны, пока вы ве закончили первый набросок проекта и можете определять более сложные проблемы и рассматривать их решение. Иногда одао проектное решение может определять или изменять несколько других. Проектирование БД всегда является итерационным процессом.
Подготовка модели для реализации Каи только мы разобрались с логической моделью так, кан только что отметили, можно заняться подготовкой наших таблиц ия создания. Здесь мы должны предпринять шаги, чтобы сделать систему легкой для создания, добавляя столбцы для оптимистической блокировки и запрета работы с записью (и что-нибудь еще, удовлетворяющее ваши специфические потребности). Затем мы добавим первичные ключи, уникальные ключи и, наконец, выберем типы данных. Как только мы завершим эти заключительные задачи, мы будем готовы к наииеаяию наших объектов ВД и реализации ВД на SQL Server, Служебные столбцы Кратко напомним, что служебные столбцы не относятся ни к каким физическим объектам ВД. Они просто включены в ВД, чтобы сделать кодирование более легким. Два примера, которые мы ранее рассмотрели, можно учесть в следующей таблице: payee ; | payeett PrimaryKey j ' name: Name (AKI ,1) ' ' [ date: disableOate [ [ I autoTimestamp: autoTimestamp I । 1 ........... .........." \ В атой таблице мы имеем столбец типа disableDate (дата с возможностью запрета) в противоположность dieableFl (флаг запрета), который мы ранее обсудили. Параметр типа disableDate устанавливает дату и время, после которого получатель платежа больше не может получать суммы денег ио сделкам в нашей ВД. Мы могли бы также добавить таблицы, чтобы хранить причины этого и историю состояния получателя платежа, но мы не будем здесь это делать, чтобы упростить задачу. Параметр типа disableDate работает так же, как disableFl, за исключением того, что вместо простого флага мы реализуем дату, так что мы можем видеть, какие будущие дату и время мы устанавливаем. Мы также добавили столбец autoT laws tamp (автоматическое время создания/обновлвиия) к каждой таблице модели, который, как мы предварительно обсудили, формирует основы механизма оптимистической блокировки. Они исключительно служебные столбцы, которые мы будем добавлять к нашей текущей ВД. Первичны» ключи Столбцы первичного ключа — фактически тоже служебные столбцы, так как мы никогда не будем показывать, их пользователю, и они существуют прежде всего для нужд нашей реалиаации, хотя мы и представили их и на логической, и на физической моделях. Возьмем следующий фрагмент модели:
bank Указатель benkld (идентификатор банка) мигрирует из таблицы bank (банк) всюду до таблицы statement!:.ег (элемежг отчета). Это прекрасно для логического моделирования, но ключи в физических, таблицах, состоящие из нескольких частей, вызывают больше неприятностей, чем имеют достоинств. Главная причина здесь заключается в том, что нам может потребоваться два, три или даже большее количество столбцов, чтобы уникально идентифицировать записи таблицы. Если вам нужно присоединить десять таблиц, чтобы найти связанную информацию, то это будет неприятна. Таким образом, если наша ВД была реализована указанным «же образом, и мы задали вопрос — В каких городах в Первом Национальном Вайке мы потратили большую часть денег на текстиль?", то этот запрос включил бы каждую таблицу, если бы мы использовали все ключи, состоящие из нескольких частей, и запрос оказался бы неуправляем. Другая причина для создания первичного ключа, состоящего из служебного столбца, состоит в том, что если вы изменяете состав лопгаеского первичного ключа для объекта, то не затрагиваете реализованный первичный ключ объекта. Это уменьшит влияние изменения на левой зависимый объект. В нашем примере возьмем набор отношения Ъапк - account - statement - staCementite»”. Если мы решили, что таблица bank больше не нужна, и удалили таблицу из модели, таблицы account, statement и statement!tern изменят свои первичные ключи: ЬапИй: йипигуМу {HQ (АК1 Л) _____________ number. WptaNu menc N umber (AKI .2) stsiement
Это — не оптимальное решение нашей проблемы кодирования, потому что весь код, который использовал эти сущности, будет теперь нарушен. Однако если бы каждая таблица имела первичный ключ, состоящий иа одного столбца, только таблицуаесоцпг нужно будет изменить. Содержание объектов изменилось бы, так как они больше ие будут определяться соответствующим банком, но для программиста фактические значения первичных ключей были бы все еще теми же самыми. Счет бы все еще содержал отчеты и сделки. tank (АН1 1 i nymten к имеет элементы s ргейоиаваэ^зса &&&& яжвйваШсе: SslsUXLll ШШКйвсм^Дгпаипг I аЩупелйШа <.. ; зШатеяЙетИ: ?rtmsn*W FrimarvtolFKj сдзсЙД1ик ^ssWon smgffirE wnou« Теперь посмотрим, что произойдет, если мы удалим таблицу bank из этой модели: account accountlc: Prim ary Key _ соанвдиуп;данными bankid: PrlmaryKey (HQ (AKI.1) number: Alpha Nu mer: cN umber (AKI ,2} 1 inb ' ::: 1 statement • statement^: Primer у Key accoumid: РгтаоКву (RQ statemeritTypeld: Рг тагу Key (FK) prariousBalanee: Amount p-e.-iousBaianceDate: Date cunwrtBelence: Amount statem e "Date Date (AK1.1J totalDebits: Amount lotalCredi’s Amount имеет алименты I t slalenenlliem >_____________ statementitemld: Prim ar у Ke, staiementld: Primary Key (FK, dale: Date number: AlphaNumsncN an bar description; Description amount: Amount Таблицы statement и statementitem не изменяются. Следовательно, программы, осуществляющие доступ к ним, также не будут изменяться. Только программы, связанные с таблицей account, должны будут измениться, чтобы отразить, что в таблице больше нет поля bankid, — незначительное изменение по сравнению е тем, что могло вы потребоваться. Обратите внимание, что, как только мы изменили ключи, реализация таблиц изменилась, хотя то, что таблицы представляют, не изменилось.
Один момент относительно новых таблиц — каждая из них имеет уникальный ключ, который содержит мигрирующую часть предыдущего первичного ключа. Например, перед внесением изменений таблица statement выглядела следующим образом: statement | statamanUd: PrimaryKey • Ь«*И; Primarjfey «(«1.2J ' accountld: Primary-fey (FK; MaternentlypeM; PrimacjKey (FK) , previousBalance: Amount j ; prevtousBalanceDate: Date j cwrentBalance: Amount I stawnentDate: Date {AK1.1J I ( totatuebts- Amount ’ i tctatCredils: Amount ( 1 __ j Эта таблица имеет не простой для определения естественный ключ, так как мы предварительно не задавали никаких вторичных ключей в нашем проекте. Вели повнимательнее посмотреть, то можно заметить, что имеется возможный ключ, который мы можем использовать — значение даты (вероятно, со временем будет удалено). Мы должны сформировать программу, позволяющую выполнять балансирование счета в диалоговом режиме, которое мы могли бы делать ежедневно, но не более часто (проконсультируйтесь с клиентом перед принятием таких решений). Итак, мы могли бы использовать дату как часть вторичного ключа, что представлено в следующей диаграмме: ( statement ! । stetementRi: ftlmatyKej I ассостИ' PrhnarjKey (FK) I staternentTypeld MmeryKey (FK) j previous8a la nce; Amount | prewusBatanceDate: Date ! currentBalance: Amount statementOate: Date (AKI .1) [ totetDebits: Amount totalCredits: Amount I . .."z.zzz:zz::zz:zz:....:iz±j Обычно мы не используем даты в наших ключах, даже вспомогательных, если можем обойтись без этого — особенно потому, что мы реализуем дату как полную зосьмибитовую величину даты-времени. Имеются две очень важных причины избегать згой практики: □ Простота обращения к данным — когда мы должны использовать ключ, чтобы полущить доступ к конкретной строке в таблице, то если наш ключ определен как значение даты, может выть довольно трудно ввести вручную фактическое значение даты. Например, значение datetime содержит день, месяц, год, час-, минуту, секунду и части секунды (тип saalldatetime включает значения вплоть до минут). Это может быть довольно тяжело, особенно для среднего пользователя, □ Точность — в большинстве случаев точность переменных, содержащих даты, больше, чем требуется для среднего ключа. Обычно, когда вы хотите поместить дату в ключ, вы просто задаете день и год, В нашем случае мы будем использовать тип datetime, вероятнее, smalldatetime, чтобы иметь возможность выполнить отчет в различные времена в течение дня, и этот тип здесь наиболее удачей. Плюс к этому, мы будем удалять некоторые из аначеяий времени.
Типы данных Во время моделирования мы выбрали логические домены для явших атрибутов. Теперь мы возьмем нага проект и преобразуем атрибуты в реальные поля и назначим реальные тигля данных. Чтобы выбрать типы данных, мы создадим таблицу выбранных нами доменов и затеи назначим тины данных и описания для них — выбор типа данных, возможность задания значения MOLL и любая дополнительная информация относительно домена. Как только мы выполним эту задачу, то пройдем по каждой иа наших таблиц, задавая типы данных значениям доменов, которые мы выбрали. Домены, которые мы должны использовать в кашей логической модели в течение этого упражнения, имеют форму: ГаЫеЫапе________________ : pkejAttrfcuteW. РпгмгуКву ' аИпШв!; ottwQomaWhme Здесь tableName — имя таблицы, pkeyAttributeld — идентификатор атрибута первичного ключа, attributel — первый атрибут. „ где primary Key (первичный ключ) и otherDanainName (имя другого домена) рассматриваются как домены для различных атрибутов. Следующий список содержит все домены, которые мы использовали по имени. Мы пройдем их и сделаем некоторые предположения относительно того, что представляют собой типы данных, а затем, когда закончим эту работу, будем использовать результаты первого просмотра для выбора наших типов данных. Мы найдем, что имеется очень хорошее совпадение, и нам не нужно будет миогне из них изменять. Однако нам будет нужно пройти все таблицы, чтобы проверить, что они правильные (конечно, здесь мы рассмотрим только несколько таблиц). Название Комментарий Тип данных Возможность I мепольаованяя | NUIX I addres sDscr iptive Line (строка описания адреса) Используется для размещения . информации об адресе. varchar<920/, чтобы размещать уникальзые индексы размером до 900 байт КОТ NULL addresaStateCode (код штата в адресе) Два символа кода штата, которые определяются почтовым ведомством ЛИА., char(2> NOT NULL al phaNuiae г i cNumber (алфавитно-цифровое число) Алфавитно-цифровое "число", которое обычно является значением вторичного ключа для записи в таблице. Пример = номер счета. varchar(20) ‘«ОТ NULL
Название Комментарий NULL Mount (сумма) Домен общего вида дая денежных величин. money NOT NULL areaCode (код региона) Код, который определяет регион штата, varchar(3) NOT NULL autcTimescamp (автоматическое определение времени создания/обновления) Автоинкрементная величина блокировки. timestamp ’ NOT NULL countrycode (код страны) Набираемы;! код. который определяет страну. , chard) •d .NULL date (дата) Используемые по. умолчанию значения даты и времени. По умолчанию saialidatetiBe, так как типа данных datetime. smaildatetime NOT NULL description (описание) Краткие неструктурируемые комментарии, описыважицве элемент таблицы, чтобы иметь возможность передать это другим пользователям. Varchar (10Q) NOT NULL diвableDate (дата запрещения) Используется, чтобы запретить работу с записью, не удаляя ее, например, для целей архивирования. snaildatetime NULL exchange (номер коммутатора) Код региона, который помещается перед номером телефона. (5) NOT NULL extension (расширение) Строка^ которая содержит рад кисел; которые могут быть набраны после выполненного соединения. -.'srehar (20) varchar(€0) NOT NULL
Название Комментарий Тип таимых Возможность использования NULL Fl (флаг) Обычно — логическая величина. Реализуется как столбец в бит, который не может содержать значение NULL и значение которого, равное нулю, означает False (ложь), а ненулевое значение — True (истина). bit NOT NULL lastName (фамилия) Используется для размещения фамилии человека. varchar(50) NOT NULL ! middleName (отчество) Используется для размещения отчества человека. varchar(60) NOT NULL name(имя) Имя, которое будет однозначно определять конкретную запись. Это — не имя человека, а скорее значение вторичного ключа для объекта. varchar(60) NOT NULL number (число) Обобщенная величина, являющаяся числом. По умолчанию целого типа. int NOT NULL phoneNwnbe rPa гt (собственно номер телефона) Номер телефона. char(4) NOT NULL primaryFl (первичный флаг) Флаг в один бит, который используется, чтобы указать первичную запись группы. bit NOT NULL primaryKey , (первичный ключ) Автоинкрементный столбец идентификации, используемый как указатель на элемент сущности. int NOT NULL, LDENTILY (автоинкре- ментный) string (строка) Обобщенные строковые значения. Мы использовали ее по умолчанию, когда не знали, какую строку собираемся использовать. По умолчанию имеет значение NOT NULL. (2D) NOT NULL
Название Комментарий Тип данных Возможность использования NULL username Используется для sysname (тип ?ст (имя пользователя) размещения тьме ни пользователя в системе. даниьпс, определяемый пользователем в SQL Server, который ясполмуетея дяя размещения имен пользователей). Мы будем размещать в SQL Server в этом типе данных имена, которые отображают реальные имена, так мы дадаи права любому пользователю, который может вводить чек, проветривать сделки „/или счета, используемые я учебном примере, char(5) «.ipCcds (почтовый индекс) Пять цифр почтового индекса, определяющего почтовый регион, который может охватывать различные города, штаты, а может также просто идентифицировать часть города. (Обратите внимание, что мы использовали zipCode* чтобы связаться е городом. Ох предстаалеи пятью символами ПОЧТОВОГО индекса. Мы игнорировали дополнительные 4 символа понтового индекса снова дяя простоты") NOT NULL Следует отметить, что все лиш адреса и номера телефонов находятся в США. Это преследует одну цель — простоту.
Как только наш список будет закончен, ми просматриваем каждую из таблиц и рассматриваем каждый тшг данных и спецификацию NUIX- для каждого поля, чтобы узидеть, как они работают в конкретной ситуации, Вели мы выясняем, что некоторые из них не соответствуют спецификациям, отмеченным выше, и решим повторить этот процесс снова, то, вероятно, захотим создать новые домены для дальнейшего использования. Однако не будем это делать; вернее, будем делать только необходимые корректировки, если потребуется. Один дополнительный момент — мы будем также иметь желание завершить определения для наших атрибутов, поскольку добавили и изменили многие нз них на протяжении проектирования. В каждом ил следующих разделов мы будем брать одну из наших сущностей, подбирать имя домена из списка, определять значения па умолчанию для типов данных и затем обсуждать и корректировать тип данных, взятый по умолчанию. Начнем с сущности bank (банк): bank bankld: PrimaiyKay гите: Name (AKI Л) □ bankld (идентификатор банка) primaryKey, int NOT NULL IDENTITY — именно то, что необходимо. Определяется как: "Не читаемый пользователем указатель для идентификации конкретного банка". □ name (название) name, varchar (60 j — это должно быть достаточно для любого названия банка. Определяется как: "Уникальное название конкретного банка". Обратите внимание, что мы используем очень формальные определения для атрибутов, так как они будут использоваться разработчиками, которые не участвовали иа стадии проектирования, и это будет первый раз, когда они увидят специфшсаншо ВД. На сталии логического моделировании (начиная с главы 4) мы определили атрибут "Bank Name" как "название банка, с которым мы имеем дело", что означает то же самое, но в более мягкой форме, и это будет понятно всем пользователям. При разработке систем с использованием средств моделирования БД вы должны будете скорректировать определения, которые мы выбрали. Наконец, мы завершаем следующим: bank___________________________ | banW* .г.С IDEN1г. папе- ,«,<-.3060) ЙОТ ?. JLL (АК1 Л) i autoTimBstamp. timesiarnp NOT NULL Обратите внимание, что мы также добавили столбец autoTimestamp, определяемый как "Автоинкрементная величина оптимистической блокировки", Мы добавим это ко всем таблицам.
Далее рассмотрим таблицу account (счет): account । accountld: PrimaryKey I han kid: Primary Key (ГК) (AKI .1} . | j number: AlphaNumericNuniber (AKI. 2) i ...--~------ □ accoantld (идентификатор счета) primaryKey, int MOT NULL IDENTITY — это точно то, что необходимо. Определяется как: "Не читаемый пользователем указатель для идентификации конкретного счета", □ bankid (вдеятификатор банка) primaryKey, int NOT NULL — а то точно то, что необходимо, поскольку является значениями внешнего ключа, не допускающего значение NULL. Определяется кек: "Не читаемый пользователем указатель для идентификации конкретного банки". □ number (номер) alphaNumericNumber, varsbar (20) NOT NULL — который должен быть достаточен почти для любого номера счета. Должны быть выполнены некоторые дополнительные исследования, чтобы определить, будет ли это годиться для всех значений, используемых клиентом. Определяется кек: "Номер стета, который формируется банком, чтобы идентифицировать счет". Обратите внимание, что, хотя значение упомянуто как число, оно фактически етяяется алфавитно-цифровой строкой, потому что может содержать ведущие нули или буквы. Это приводит к: I account i | ] accountld: int IDENTITY i i bankid int NOT NULL (ГК) (AKI 1) ' j number varcharfZO) NOT NULL (AKI .2) [ I autoTimestamp: timestamp NOT NULL ( Для нашей последней иллюстрации рассмотрим таблицу transaction (сделка): transaction : transactionfd: PrimaryKey i accountld: PrimaryKey УК) (AKI Л) date: Delfi ; number: AlphaNumericNumber (AK1,2) i description: Description i ; amount: Amount ; signature: String j payeeid: PrimaryKey (FK userid. PrimaryKey (FK) [ , statementltemld: PrimaryKey (FK) I : transactionTypeld: PrimaryKey (FK) j I . ZZ “ . __ “T- ZZ _
О trsnsectianld (идентификатор сделки) primaryKey, int NOT NOLL IDENTITY — это точно то, что необходимо. Определяется как: "Нс читаемый пользователем указатель для идентификации конкретной сделки”. □ accountld (идентификатор счета) primaryKey, int NOT NULL — это точно то, что необходимо, поскольку является внешним ключом, не допускающим значение NULL. Определяется как: "Не читаемый пользователем указатель для идентификации конкретного счета". □ пииЬвг (номер) alphaNumericNumber, varchar (20) NOT NULL — который должен выть достаточен почти для любого номера чека, номера депозита или кредитной карточки. Должны быть выполнены некоторые дополнительные исследования, чтобы определить, будет ли это годиться для всех значений, используемых клиентом. Определяется кан: "Номер, используемый в счете для идентификации сделки". Обратите внимание, что, хотя значение упомянуто как число, оно фактически является алфавитно-цифровой строкой, потому что может содержать ведущие нули или буквы. О description (описание) description, varchar (100) NOT NULL — в этом случае мы должны, вероятно, выделить дополнительное место, чтобы описать использование чека, кредатнон карточки или депозита. Однако у нас также есть таблица checfcAI location, где использование детализируется. По этой причине мы изменим таи столбца описания на varchar (10Q0I, чтобы дать свободу для описания деталей сделки, если потребуется. Определяется как: "Неструктурированные комментарии, описывающие конкретную сделку, которые в дальнейшем могут быть расширены для других возможностей пользователей, чтобы характеризовать сделку". □ amount (сумма) amount, money NOT NULL — это будет достаточно, чтобы описать сумму сделки. Определяется как: "Количество денег, которые должны быть перемещены с помощью сделки". □ signature (подпись) string, varchar (20) NOT NULL — возможно, следует расширить строку, чтобы разместить полное имя. Мы также могли бы пожелать использовать тип данных image (изображение), чтобы хранить образец подайся, В наших таблицах мы просто изменим тип е varchar (20) на varchar (100), чтобы позволить пользователю вводить полное имя человека, кому принадлежит подпись. Определяется как: "Текстовое представление подписи, которая была использована в сделке”, □ payeeld (идентификатор получателя платежа) primaryKey, int NOT NULL — это не совсем то, что необходимо, так как оно — необязательное отношение. Поэтому мы зададам столбцу характеристику null. Определяется как: "Не читаемый пользователем указатель для идентификации конкретного получателя платежа, который может быть заполнен лишь на основании значения allowsPayeeFl (флаг разрешения иметь получателя платежа) в связанной таблице transactionType (тип сделки)". Q userid (идентификатор пользователя) primaryKey, int NOT NULL — не является необходимым, так как это — необязательное отношение. Поэтому мы установим столбцу характеристику NULL. Определяется как: "Не читаемый пользователем указатель, используемый для идентификации пользователя, который отвечает за сделку".
□ statementitemid (идентификатор элемента отчета) primaryKey, int NOT NULL — не является необходимым, так как это — необязательное отношение. Поэтому мы установим столбцу характеристику Li LL. Определяется как: “Не читаемый пользователем указатель, используемый для идентификации statement Iхеш. (элемент отчета), который используется для конкретной сделки". □ transactionTypeld (идентификатор типа сделки) primaryKey, int NOT NULL — это точно то, что необходимо, так как это обяватеяьвое поле, Определяется как: "Не читаемый пользователем указатель, используемый для идентификации записи типа сделки, который определяет тип конкретной сделки". Завершим это следующей таблицей: : ~ -у transaction, : transaction^ int NOT NULL ’ i i acccuntW? mt NOT NU11. (FK) (AKI. i p ! date: smalfDateTime NOT NULL I number: va-:nar(20; NOT NULL (AKI.2} j ! description; varchar(1000) NOT NULL j j amount money NOT NULL j ' S'unatiire' varchargO) NOT NULL ; pa у eel d int NULL (FKJ j userid: int NULL (FK) i statemendtemld: int NULL (FKJ i ИопзатоопТурекк int NOT NULL (FK) i j autoTimeStamp: ttmeswmp NOT NUl.l • Таким же образом рассмотрим все таблицы, пока не определим все типы данных и формулировки. В следующих двух разделах мы покажем окончательную физическую модель, которую будем реализовывать, учитывая описания трех таблиц, которые мы создали в этом разделе. Полное описание схемы (и сопровождающий код) размещено в www.wrox.com. Прежде, чем мы продолжим, следует сделать примечание относительно сопоставления типов данных (наборы символов и упорядочивание, которые мы сформировали ранее). Задание индивидуальных сопоставлений для столбца — весьма продвинутая тема, и полезна для определенного набора ситуаций. В нашем учебном примере мы не будем использовать сопоставления, на я упоминаю их здесь снова, чтобы напомнить вам, что возможно установить сопоставление для отдельного столбца, если потребуется. Физическая модель На данном wane следует создать модель на бумаге для. "проверки здравого смысла", предпочтительно кем-либо из сотрудников, если это возможно,, чтобы можно было ' гарантировать, что ваша модель яе только эффективна и годи* к употревленвю, ио также и понятна кому-то еще. Итак, мы подходим к представлению окончательной физической модели нашей СУВД. Мы должны иметь в виду, что это модели, ио которым мы создадим и реализуем сервер ВД,
_________________ пяж vs-спанбСи NOT ЖЗД 1 m^Tfmsatafl'.u: >ЮТЖ?Ц. -ame: vsrcharJSC) NOT NULL {AK1J j “ dgsciMsfc Ш1ЦАХ21 j hot hull предлагает nnnfiRpWBWTSS GQ-асоввнным с записке* в wsoym ааооапВЙ: Int OENTiTY banker Int NOT NiJlL (HQ (AKI Л) number /arcnarrSO; MOT NULL (АЮ ai^Wneatani^ NOT WLLr r r яй ЮЭЯТГГ ассешеа-: imnar жза отд iaki ц fSt8memTTW«: tm НОТ шй. (FK} ЖэЛмжВЛпжпЩцШТ MJLL WT МЙХ йжег^Шп^гпрг^НОТЖЖА. ййа: вггвЮвжТиздНОТ HUtiym .2) t^asaebds: mswNDTNUU WOW; mone.- HOT HULL peroGt^cDats. стаВОаШГет NOT ML 8СТаТгм-Ээтр:Ш^1Лгф NOT NULL кжоийадФУв’’ >'сгт-ьэуе-ся днясот»асоаамия сче-а с помощью __________________________________________ имеет агтшнты в traraac^cn iSSSuntRsDongW int OTFOTf econcNeDa'e: anaMMeteseNOTMJU. (*K, вЛ!^ НОТ HULL sslsmsnl'errrMr tat ©arnrv ЙЬЙпвЖЙНОГИиЦ,<₽КЙЙ«-1) «: sma uateTJme NOT HUU. n.mbe-r а>шлаГг2у' NOT NULL t*K1.2l «hseripte wm*«(100) NOT NUU- mount Ttmtvf NOT ИШ. ГепзаЙои^реИ: Int WOT NULL (FK) LWrtestams: timestamp NOT NULL aocounW: int NOT №ЯХ(Ж) (Ж1 Л) Лак впавОаИПтвЫОТНии. number, varcnapjni дат MULL ftm• eescnptinn rarchefioai} NOTNUU. amount: mone^ Ж1Т NULL earalu's varcha^B} NOT NULL p^eeM: tat NULL;» KJ uswMiMNUUfFK) sutsmentSem»: NULL IFXI tгагввЯ(ОпПдаИ: int NOT NUU. (Я9 t- autoT.c'eSamp: NOT ЯЩ. итомжзда согласоваэшя кЛВСС^фи^руе' ш ю=нт!гг тж wrfwtfiB NOT NULu (Ж1Л} v^vharlWGlHQTHUiXiAKa.Ti тфй^Й1дг«исеЯ4й&ШТШ1Х ШДйТжщ^^Ш»»йф^ОТШи крржтаруёт баляанс счета, аапреийшая Tscr________у utar№‘тИШПт utsmtmt; щгмгм NOT NUU, (AKJ.1) firstName: varetarteo) NOT NULL ткМвНатвп уашлаг'бО) NOT MJU iMtNume: va rec ar j в0| NOT NUU. autoTimaemmp: втаяапф NOT MMX ШааСЩ^СЙаЛ^ ««ват информадям распределении размешен» ус с да: wrstwffiQl ониа зеку1рй&- ^сйвгПВД ГЮТ №ЛХ p^eOTCnecHUsage vpe-d: int NOT Ш!1 (Rg autoTgnegwnp: Sagma >ЮТ ЖМХ сассифишфугг чеке помощью _______________ :гапжп»гЛ1зса#й'йуя13: a not NULL(«) (AKI Л) и ansacuonla r tat NOT MM (RQ (Ж1Д ) allocationAmovnt: tnpnny NOT NULL aii-oTimestiimp «шииту NOT tWU. является
Резюме Да, это Выла длинная глава, охватывающая Волыпое количество понятий. Она переполнена информацией, которая является очень важной для каждого читателя. Понимание, как строить таблицы и как они реализуются — основа званий каждого проектировщика ВД. Мы взяли нашу логическую модель и исследовали каждую сущность, чтобы определить, насколько возможно их реализовать. Особо были рассмотрены подтипы, поскольку о ними могут быть проблемы. Мы также рассмотрели возможные отклонения от наших строгих правил нормализации в особых случаях (хотя мы боролись с этим в максимально возможной степени), и очистили структуры нашей ВД от ошибок, которые имеются в модели. После того, как мы удостоверились, что имеем именно ту модель, которую могли бы реализовать, мы попристальней взглянули на таблицы SQL Server, прошлись по ограничениям таблиц и рассмотрел» синтаксис операторов CREATE TABLE и ALTER TABLE. Выли рассмотрены также правила обозначения объектов, столбцов, индексов и ограничений внешнего ключа. зев
Два наиболее важных раздела агой главы были посвящены типам данных и индексам. Когда мы реализуем наши таблицы, очень важно понять концептуальный способ, которым наши данные будут фактически размещены в SQL Server. Закончен этот процесс определением первичных ключей ж добавлением, по крайней мере, по одному вторичному ключу на таблицу и столбцу типа t хяе st агор для целей оптимистической блокировки. Окончательиая модель и описание, как реализовать систему, доступны на Web-сайте Wrox Press. В следующей главе мы завершим задачу реализации ОЬТРсистемы, обеспечив бизнес-правила, необходимые, чтобы содержать данные в нашей БД настолько чистыми, насколько возможно.
Обеспечение целостности данных Введение В этой главе мы займемся предикатами и доменами, которые выли определены во время проектирования..Когда мы обсуждаем целостность данных, то рассматриваем применение основных правил к данной таблице или набору таблиц, которые гарантируют, что размещенные данные всегда полноценны. Поддержание нашей ВД в состоянии, когда данные всегда соответствуют нашим исходным техническим требованиям проекта, является основой большинства разработок, Независимо от того, насколько хорошо иди плохо разработаны таблицы, как это воспринимает пользователь, ВД всегда будут определяться тем, насколько хорошо защищены данные. При разработке стратегии целостности данных мы должны рассмотреть несколько различных сценариев: - - □ пользователи, применяющие традиционные клиентские средства; □ пользователи, применяющие настраиваемые средства работы е данными типа Microsoft Access; □ программы, которые импортируют данные на внешних источников, □ необработанные запросы, выполняемые администраторами данных, чтобы выявить проблемы, связанные с ошибками пользователя. Каждый из яих обладает различными проблемами по отношению к нашей схеме целпетиости и, что более всего важно, каждый из этих сценариев (может быть, аа исключением второго) составляет честь каждой СУБД, которую мы разрабатываем. Чтобы лучше обеспечить каждый сценарий, мы должны защитить данные, используж механизмы, которые работают независимо от пользователя и которых нельзя случайно обойти, Имеются четыре возможных пути, как мы можем использовать код, чтобы обеспечить непротиворечивые данные:
□ Типы данных и ограничения являются простыми непосредственными средствами, которые составляют неотъемлемую часть определения таблицы. Они являются очень быстрыми средствами, которые требуют немного кодирования, и их использование может дать существенное улучшение функционирования. Такие ограничения включают действия типа определения диапазонов данных в столбцах (например, элементы столбца должны быть больше, чем 10, или — если значение в столбце 1 равно 1, то значение столбца 2 больше 1). Это — лучшее место для защиты данных, поскольку большая часть работы выполняется с помощью SQL Server, и пользователь не сможет обойти это при любой ошибке во внешней программе. □ Триггеры отличаются от ограничений тем, что они являются частями кода, который вы добавляете к таблице (или таблицам). Этот код автоматически запускается всякий раз, когда происходит событие (события), которое (которые) вы определили для таблицы (таблиц). Ойи чрезвычайно гибки и могут обращаться с несколькими столбцами, несколькими строками, несколькими таблицами и даже несколькими БД. Как простой случай, рассмотрим ситуацию, где мы хотим гарантировать, что обновление значения выполняется в обеих таблицах, Иде находятся данные. Мы можем написать триггер, который запретит обновление, если оно не происходит в обеих таблицах. Это — второе лучшее место, чтобы защитить данные. Мы должны закодировать каждое правило триггера я Т-SQL, но пользователь не сможет обойти триггер при любой ошибке во внешней программе. □ Хранимые процедуры — части кода, которые размещены в БД и очень гибко могут работать с таблицами; например, использование различных бизнее-иравил’при различных обстоятельствах. Простой пример хранимой процедуры — которая возвращает все данвь», находящиеся в данной таблице. В более сложном сценарии они могут использоваться, чтобы давать различные разрешения различным пользователям относительно работы с таблицами. Это — не лучшее решение для защиты данных, поскольку мы должны закодировать правила в какой-то процедуре, которая имеет доступ к данным. Однако та» как ока находится в центральном месте, мы можем использовать ев, чтобы реализовать правила, которые могут быть различными в разных ситуациях. □ Выполняемый клиентом код — полезен в ситуациях, когда бизнес-правили необязательны или гибки по своей природе. Довольно общий пример, когда SQL Server формирует вопрос пользователю ‘Вы уверены, что желаете удалить эту запись?” и если вы просите удалять данные, он их удалит. Большинство результатов работы сервера выполнено в этой манере, оставляя программам пользователя возможность реализации гибких бизнес-правил и предупреждений. Имейте в виду, что приложения приходят и уходят, но данные должны быть всегда защищены. При перемещении вниз по атому списку решения становятся менее желательными, но все же каждое имеет определенные выгоды, которые полезны я некоторых ситуациях. Все эти особенности будут рассмотрены в свое время, однако мы начнем о новой особенности в SQL Server 2000, которая является существенной при формирования ограничений, триггеров и хранимых процедур, Эта особенность — определяемые пользователем функции (User Defined Functions).
Примеры таблиц В примерах дайной главы, предназначенных для иллюстрации различных концепций, мы будем использовать следующие таблицы: CREATE table artist I artlstld int NCT NULL IDJUTITM, name varchar(ovj, — зштте, «о первичный х-зч является кластерным, -- что Ф"„ь: -ас’мотреио позже * этой главе CONSTRAINT NFKarcist PRIMARY KEY CLUSTERED (artistld), CONSTRAINT XAKartiBt_naae UNIQUE NONCLUSTERED (name) INSERT INTO artist(name) VALUES ('the beatlee'i INSERT INTO artist(name) VALUES- (' the who') GO CREATE TABLE ахваж ( albumld int NOT NULL IBESTITY, name varchar(60), artietld iot -NOT NULL, - заметьте, что листай ключ является кластерном, — чтс- будет рассмотрено позже в этой главе CONSTRAINT XFKalbum PRIMARY KEY CLUSTERED(albumld) , constraint XAKslbumname unique nonclustered(name), CONSTRAINT art!stSrecordsSalbum FOREIGN KEY(artistld) REFERENCES artist j.NSEkT INTO album (name, artistxd) VALUES ('the white album.', 1) INSERT INTO album (name, artistld) VALUES ('revolver', I) INSERT INTO album (name, artistld) VALUES ('quadrophonia* , 2) Здесь создаются две таблицы. Первая таблица — arrist ( артист Д содержащая два столбца: первый — artdstJd i идентификатор артистаавтоин^ементный целого типа, который не мажет принимать значение NOLL, и второй — папа (имя) типа varcbar (60), Для этой таблицы, -задаются ограничения: XPKartist (первичный ключ таблицы artist), представляющее собой кластерный первичный ключ, использующее столбец artistld, и ХМЛЛ1зС_пшав (вторичный ключ — имя артиста), обеспечивающее некластерный столбец пте, содержащий уникальные значения, В эту табмщу: помещаются две записи, содержащие в столбце пате строки "the beatlee" (Витлз) и "the who" (Ху). Вторая таблица album (альбом) содержит три столбца: albwld (идентификатор альбома), автоинкрементный целого типа, который не мажет принимать значение NULL, вт^рй — name (имя) типа varchar (60) и третий-— art! st id целого типа, который не мажет принимать значение NULL. Для отрй. таблицы -задаются ограничения: XPKalbuM (первичный ключ таблицы album), представляющее собой
кластерный первичный ключ, использующее столбец albmld, XAKabbum_nar№ (вторичный ключ- — название альбома), обеспечивающее некластерныЛ столбец name, содержащий уникальные значения, и ограничение artistfrecordsSalbua (артист записывает, альбом), использующее столбец artistld 9ля ссылки на таблицу artist. В зту таблицу помещаются три записи, у которых в столбцы пате и artistld будут помещены записи: "the white album", 1; "revolver", 1 и "puadrophonia", 2. Определяемые пользователем функции Определяемые пользователем функции (ОПФ) (User Defined Functions — UDF) могут применяться, чтобы гарантировать целостность данных, и (по моему мнению) являются одной из наиболее важных особенностей SQL Server 2000. Обычно ОПФ тесно связаны с хранимыми процедурами, но вместо того, чтобы быть средствами, в первую очередь предназначенными для корректировки данных и возвращения результатов, они являются T-SQL-программами, которые возвращают значение. Как мы увидим, компилятор SQL Server не позволит вам выполнить внутри ОПФ любой код, который модифицирует данные, и поэтому ОПФ может лишь использоваться для операции, выполняющих только чтение. Возвращаемое ОПФ значение может быть или простой скалярной величиной, или таблицей, или набором результатов. Что делает их сказочно полезными — то, что они могут быть вложены в запросы, таким образом сокращая нашу потребность в огромных запросах со многими строками повторяющегося кода. Единственная функция может содержать программы, которые могут вызываться из запроса по желанию, В оставшейся части втой главы и в следующей мы будем широко использовать ОПФ, чтобы решать задачи, которые в более ранних версиях SQL Server было или невозможно, или очень трудно решить. ОПФ имеют следующую основную структуру: I CREATE FUNCTION [<имя_г.ольэователя>. ]<имя _фу.нкции> ( Г<параметры> j — точно так же, как у ссансслс-; процедур ) KETQRfJS <тип_данных> AS BEGIN [<оператры>] RETURN <выражение> END При создании этих функции в противоположность хранимым процедурам имеется несколько очень важных различий в синтаксисе, которые мы должны помнить: □ Мы используем CREATE FUNCTION вместо CREATE PROCEDURE. □ Круглые скобки всегда требуются, независимо от того, имеются ли параметры. □ Операторы RETURNS используются для объявления типа возвращаемых данных. Обратите внимание, что оператор RETURNS является обязательным для ОПФ в отличие от хранимой процедуры. □ BEGIN и END требуются для скалярных функций и функций, состоящих из набора многих операторов. Имеются три разных версии ОПФ, которые мы рассмотрим ниже.
Скалярные функции Скалярные функции возвращают единственное значение. Они могут использоваться в различных местах, чтобы реализовать простые или сложные функциональные возможности, не имеющиеся обычно в SQL Server. Наш первый пример скалярной функции — один из самых простых возможных примеров: увеличение значения целого числа. Функция просто берет единственный целый параметр, увеличивает его на единицу и затем возвращает это значение: CREATE FUNCTION integerStn - • e~-.r ~ I iintegerVal int RETURNS int BEG IК SET SintegerVal = BintecerVal + i RETUBK SintegerVal END Обратите внимание, что я снова использовал для названия функции знак доллара. Я называю все функции, триггеры и хранимые процедуры в такой манере, чтобы отделить объект (в этом случае integer — целое число) от метода (в данном случав — increment — увеличение). Это — просто мое личное предпочтение. Мы можем выполнять эту функцию с помощью классического синтаксиса выполнения из предыдущих версий SQL Server: DECLARE Binteger int EXEC вinteger • integerMncre®ent SintegerVal ~ 1 SELECT iinteger AS value ... который возвратит: value 2 Теперь, когда мы создали функцию и выполнили ее, логично пожелать вызвать ее из оператора SELECT. Однако это более хитро, чем могло бы вначале показаться. Если мы попробуем выполнить следующий оператор: Ц SELECT integerSincrement(1j AS value ... то получим следующее сообщение: Server: Msg 195, Level 15, State 10, Line 1 'integerSmcrement' fg not recognized function name (Сервер; сообщение 195, уровень IS, состояние 10, строка I ’integerfincremeat' — неопознанное имя функции)
ОПФ требует, чтобы вы определили владельца функции для ее уснедшого выполнения. 3 SELECT dbo.integerfincreaent(1) AS value Теперь мы получим желаемый результат: value 2 Имеется еще одно замечание по использованию, о котором мы должны упомянуть. Вы не можете передавать названные пользователем параметры функции, как вы можете поступать с хранимыми процедурами. Так, следующие два метода вызова кода не будут работать: SELECT dbo.integersincrement SintegerVal - 1 AS value SELECT dbo,integerSincrement (8integerVal - 1) AS value Выполнение их дает: Server: Msg 170, Level 15, State 1, Line 1 Line 1; Incorrect syntax near 'integerVal'. Server Msg 137, Level 15, State 1, Une 3 Must declare the variable '©imegerVaf. (Сервер: сообщение 170, уровень 15, состояние 1, строка 1 Строка 1: Неправильный синтаксис в окрестности ((bintegerVaL Сервер: сообщение 137, уровень 15, состояние 1, строка 3 Должна быть объявлена переменная @tntegerVal.) К сожалению, только первое сообщение об ошибке имеет какой-либо смысл (второе продет передать логическое значение (SintegerValue = 1) в функцию). Странно, но мы можем использовать синтаксис "eparmname = " только с оператором ЕХЕС, как видели в примере выше. Как заключительный пример, использующий функцию увеличения, мы три раза увеличим значение в одном и том же операторе (обратим внимание яа вложенные вызовы): SELECT dbo. integerSincrement (dbo. integerSincrement (dbg. integerSincreinent (1) '} AS valve ... что, как ожидается, возвратит value 4 Давайте рассмотрим теперь более сложный пример функции, который, как ж уверен, многие читатели хотели бы реализовать: функция, которая берет строку и пишет прописную букву в начале каждого слова внутри этого текста;
CREATE FUNCTION stringSproperCase ( SinputString varchar(2000j RETURNS varchar(2000) AS BEGIN — устанавливаем see буквы строчными SET ginputString = LOWER(Йinputstring) — затем используем процедуру stuff (вставить) для первого символа SET einputstring - — вставляем прописную букву следуицего символа -- вместо строчной буквы STUFF(einputString, 1, 1, UPPER{SUBSTRINGffiaputString, 1, 1J)J —в! - параметр цикла, инициализируежЛ значением 1 DECLARE Si int SET Bi - 1 — цикл от второго символа до конца строки WHILE в! < LEW (iinputStrlrig) BEGIN — если символ - пробел IF SUBSTRING(iinputString, Bi, 1) - ’ ' BEGIN — помещение прописной буквы иа место следующего символа SET SinputString - STUFF(ginputString, ii ♦ 1, 1, UPPER(SUBSTRING(ginputString, 61 + 1, 1))) END — увеличение параметра цикла set ei - ei + i END RETURN einputString END GO Здесь в переменную ginput-Strlng (вводимая строка) типа varchax(2QO0) при обращении к этой функции помещается преобразуемая строка Далее с помощью стандартной процедуры LOWER все буквы строки преобразуются- в строчные. Затем первый символ строки преобразуется в прописную букву. Для этого с помощью стандартной : функции SUBSTRING выделяется строка, начиная с первою символа и длиной в один символ (фактически выделяется один символ), которая с помощью стандартной функции UPPER преобразуется в прописную букву, которая опять-таки с помощью стандартной функции. STUFF замещает первую букву строки. Даме организуется цикл просмотра всех символов с первого до предпоследнею (длина строки определяется е помощью стандартной функции LEN). Если очередной символ является пробелом, то следующий символ преобразуется в прописную букву точно так же, как и первый символ строки. Функция возвращает преобразованную строку. Итак, мы можем затем выполнить следующий оператор: I SELECT name, dbo.stringSproperCase(name) AS artistFroper FROM artist
и получить старые названия и новые. name artist the beattes The Beatles the who The Who Хотя это и не совершенная функция для изменения строки добавлением заглавных бутсе, мы могли бы легко расширить ее иа "специальные" случаи, которые могут возникнуть (подобно McCartney или MacDougall). Последний пример, который мы рассмотрим, — доступ к таблице альбомов, подсчет числа записей и возвращение числа записей. Что хорошо в этой функции — то, что вызывающий не будет знать, что осуществляется доступ к таблице, так как эта сделано изнутри ОПФ. Если мы создадим следующую функцию: CREATE FUNCTION albumSrecordCount ( famistld int 1 RETURNS int AS BEGIN DECIARE gcount AS int -- переменная для выходной величины SELECT gcount « count {*} FROM album WHERE artistld - eartistid RETURN Scount EMD GO Эта функция возвращает число альбомов (в параметре count), находящихся в таблице album для конкретного артиста, определяемого идентификатором артиста ( Sartistld). ...то выполнив ее е помощью единственного оператора SELECT: Н SELECT dbo.albumSrecordCeant(1) AS albumCount I FROM album будет получено: aibumCount 2 Однако если мы выполним ее следующим образом: И SELECT name, artistld, dbo.albumSrecoedCountfartistldj AS aibumCount | FROM album
будет возвращено: artistld aibumCount 1 2 2 1 ОПФ будет выполняться один раз для каждой строки. Это не проблема для такого маленького запроса, но выполнение станет проблемой, когда мы будем иметь большое число больших таблиц. Встроенные функции формирования таблицы Встроенная функция формирования таблицы, также известная как функция формирования таблицы с единственным оператором, является более сложным вариантом скалярной функции, так как она возвращает таблицу, а не единственное значение. В следующем примере кода мы строим функцию, которая возвращает таблицу ключей альбоме» для конкретного артиста. Мы волжмы обра?нить внимание на последовательность, с которой жы оврящаввся. к функции, возвращающей таблицу, когда набор результатов получается из процедуры, I CREATE FUNCTIOR albumSreturnKeysByArtiet ( tjartistld int ) RETURNS TABLE WITH SCHEMABINDINS AS RETOR» ( SELECT albt»Id FROM dbe.album WHERE artistld - gartistMf Функция возвращает таблицу table, точнее me ее записи, у которых значение идентификатора записи artistldравно значению передаваемого в функцию параметра Qartistld. Теперь мы можем выполнить функцию. Но как? Это полностью отличается от любого из ресурсов, с которыми мы имели дело в SQL Server до настоящего времени. Мы не можем выполнить ее как процедуру: Ц EXEC dbo.albweSreturnKeysByArtist gartistld « 1 Такой оператор возвратит ошибку: Server: Msg 2809, Level 18, State 1, Line 1 The request for procedure ’albumtretumKeysByArtisf failed because 'albumSref urnKeysByArtist' Is a function object. (Сервер: сообщение 2809, уровень 18, состояние 1, строка 1 Обращение к процедуре'albuoifretarnKeyeByArtUf потерпело неудачу, потому что 'albumXreturnKeg»ByArtist‘ — функция.}
Что мы можем сделать, одиако, так это подставить вызов функции формирования таблицы вместо таблицы в любом запросе. Поэтому мы выполним функцию подобно следующему: I SELECT album.albumld, album.name FROM album JOIN dbc . albumSxeturr.KeysByArtlst! 1 > AS functionTest OK album.albumld » functionTest.albumld что даст следующее albumld name 1 the white album 2 revolver Является ли это лучше, чем обычное присоединение? Нет, конечно. Это не стандартная операция программирования ВД, и оне, вероятно, препятствовала бы оптимизатору запросов сформировать наиболее оптимальный код. Однако это — симпатичный свежий способ построить специальную часть кода, который мы можем добавлять к нашему набору средств, если имеем невольшие навары .данных., которые хотим исиельаивать в наших запросах. Связывание схемы В объявлении нашей функции, которую мы рассмотрели только что, содержится незнакомый термин: I WITH SCHEMABINDING Связывание схемы — новая особенность, которую SQL Server предоставляет для функций и представлений, позволяя врать элементы, от которых зависит ваше предс тавление (типа таблиц иля представлений). и "связывая" их в это представление. Таким образом, никто не может сделать изменений в этих объектах, пока схема связывания представления не удалена. Это создает довольно общую проблему, которая возникает в первые же дни программирования системы клиент-сервер. Что произойдет, если изменится сервер? Как клиент будет работать при таком изменении? Например, в нашей созданной функции мы использовали таблицу альбомов. Если мы хотим удалить таблицу альбомов: | DROP table album то получим следующую ошибку: Server: Msg 3729, Level 16, State 1, Line 2 Could not DROP TABLE 'album'. It is being referenced by object ‘albumSreturnKeysByArtist’. (Сервер: сообщение 3729, уровень 16, состояние 1, строка 2 Нельзя удалить таблицу 'album. К ней, нужно обращаться как к объекту 'aTbumSreturnKepsByArtist'.') Обратите внимание, что это — неплохая вещь. Действительно, данный пример демонстрирует встроенный механизм защиты SQL Server своих данных.
А что, если мы хотим просто изменить таблицу? ОПФ отвергает любую попытку изменить используемый столбец, хотя мы будес-t иметь возможность добавить столбец или изменить неиспользуемый. Чтобы проиллюстрировать это, мы добавим столбец и затем удалим его, чтобы оставить таблицу в первоначальном состоянии: I ALTER TABLE album ADD description varchar(100; NULL ALTER TABLE album DROP COLUMH description ... что успешно будет выполнено. Однако если мы попытаемся удалить столбец, который используется функцией, например, artist Id*. I ALTER TABLE album DROP COLUMN artiatld ...to SQL Server знает, что этот столбец используется функцией, и сообщит нам: Server: Msg S074, Level 16, State 3, Une 1 The object 'atbumSreturnKeysByArtst' is dependent on column ‘artisUd'. ALTER TABLE DROP COLUMN artlstM failed because one or more objecte access this column. (Сервер: сообщение 5074. уровень 26, состояние 3, строка 1 Объект. 'aibumSreturnKcysByArtist' зависит от столбца- 'artlstH, ALTER TABLE DROP COLUMN artistld терпит неудачу, потому что один или несколько объектов используют этот столбец.) Вы должны всегда использовать связывание схемы при создании системы, к которой будут обращаться клиенты. Очевидно, если вы собираетесь часто удалять и заново создавать таблицу, то связывание схемы не могло бы дать вам существенных преимуществ, жо большинство таблиц в разветвленной БД будет редко, если когда-либо, изменяться, и если это произойдет, всегда благоразумно знать код доступа к ним. Не будет никаких проблей функционирования креме скорости, с которой объект, использующий связывание схемы, может быть изменен. Если вы должны изменить таблицу или столбец, то можете: □ выключить SCHEMABINDING, выполнить требуемую корректировку и снова вернуться назад: □ удалить функцию, изменить таблицу или столбец (столбцы) и затем заново создать связывание схемы. Объект может быть схемно-связанный, если выполняются следующие условия: □ все используемые объекты с хемно-связанные; □ ко всем объектам можно получить доступ, используя две части названия (owner.objectHame); □ все объекты находятся в одной и той же ВД; □ пользователь должен иметь соответствующие разрешения для обращения ко веем объектам в коде. 13 -!>М «7
Например, если мы попробуем изменить нашу функцию следующим образом: I ALTER FUNCTION albu“5re'urnKey»ByAetist ( Sartistld int J RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT albumld FROM album — должно быть abo.album WHERE artistld = gartistldj Это вызовет следующую ошибку: Server: Msg 4512, Level 16, State 3, Procedure album4retumKeysByArtist, Une 9 Cannot schema bind function 'a!bum$reiurnKeys8yArtisf because name ’album’ is invalid for schema binding. Names must be two-part format and object cannot reference itself. (Сервер: сообщение 4512. уровень 1g, состояние 3, процедура albums ret иг nKeysByArtist, строка 9 Нельзя использовать связывание схемы для функции ”albumSreturnKeysByArtist", потому что имя ’album" не подходит для связывания схемы. Имена должны иметь формат из двух частей, а объект не может ссылаться на себя} Чтобы удалить связывание схемы, просто выполните ALTBR FONCTIOW без одерядии WITH SCHEMABINDING; иапрвмер, возьмем первую функцию, которую мы создали: CREATE FUNCTION elbumSretumKeysBy Artist t lartistld int I RETURNS TABLE WITH SCHEMABINDING AS RETURN( SELECT albumld FROM dbo.album WHERE artistld - lartistld} Чтобы изменить ее для запуска без связывания схемы, мы должны выполнить этот код следующим образом: I CREATE FUNCTION albumSreturnKeysByArtist ( Bartistld int RETURNS TABLE AS RETURN( SELECT albumld FROM dbo.album WHERE artistld — Sartistld)
Функции со многими операторами, формирующие таблицы Если встроенные функции формирования таблицы представляли собой воле® сложные скалярные функции, то функции со многими операторами, формирующие таблицы, еще более сложны! Что делает их важными, — это то, что вы можете определять функции с неограниченным числом команд и строить еданственную таблицу, содержание которой мы определяем в объявлении функции. В вашем примере мы возвратим все альбомы для конкретного артист* и зададим все названия прописными буквами: CREATE FONCTIOK album?returnNamesIntJpperCase ( gartlstld int ) RETORHS SoutTable table ( albumld int, name varchar(60) ) WITH SCHEMABINDING AS BEGIN — добавление всех альбомов в выходную таблицу INSERT INTO SoutTable SELECT albumld, name FROH dbo.album WHERE artistld = lartistld — второй неиужиьй оператор UPDATE введен, чтобы показать, — что это функция со у.-.с гиии операторами UPDATE BoutTable SET name UPPER (name) RETURN END GO Когда мы выполним следующий оператор: Ц SELECT * FROM dbo.albumSreturnNameelnUpperCase(I) ... то получим: albumld name 1 THE WHITE ALBUM 2 REVOLVER Использование таких функций во многом похоже на использование функций формирования таблиц с- единственным оператором, но ояи открывают множество новых возможностей. Операции, которые ранее выполнялись, используя временные таблицы с собственным использованием кода, могут теперь выполняться в функции, причем они могут многократно использоваться и без кодарования беспорядочных временных таблиц. Что определяемые пользователем функции не могут делать Имеется несколько моментов, которые мы не можем выполнять с помощью ОПФ. Первое, на что следует обратить внимание, — имеется несколько важных встроенных в SQL Server функций, которые не могут выть включены в ОПФ. Прятаны этого довольно просты: они не
являются детерминированными. (Детерминированные функции — это функции, .которые при выполнении с конкретным набором параметров всегда возвращают тот же самый набор значений.) Аргументажия этого очевидна. ОПФ может использоваться для построения вычисляемых столбцов или столбцов в представлениях, и каждый из них может быта индексирован. Индексация может происходить только тогда, когда дли столбцов можно гарантировать, что возвращаются одни и те же значения ври любом вызове функции. Следующая таблица перечисляет недетерминированные функции, которые не могут использоваться в ОПФ. I8C0NNBCTI0NS (число соединений) gtTOTAL_ERRORS (общее число ошибок дискового чтения/записи) ввС₽и_В03¥ (врем* ЦП, занятое сервером е момента последнего запуска сервера) 98TQTAL._READ (тасло запросов ва чтение е диска) ЗвIDLE (время простоя сервера с момента его последнего запуска) йвТ0ТАЕ_Ж1ТЕ (число запросов на запись на диск) Й81О_ВШХ (время операций чтения/записи с момента последнего запуска сервера) 0ймах_сом1 :::з: i (максимальнее число одновременных соединений) М PACKJIECEIVED (число пакетов, прочитанных сервером иа сети) MPACK_SENT (число пакетов, посланных сервером в сеть) в Й RACKETERRORS (число ошибок передач пакетов) оу. (число микросекунд на тик) TEXTPTR (указатель на текст типа text, ntext или image) NBWID (создание значения уникального идентификатора типа uniqueldentifier) RAND (случайная величина) GETDATB (текущие системные дата и время) GETOTCDATE (текущее время по Гринвичу) Мы не будем вносить в список все функции SQL Server и их детерминированность. Это полностью рассмотрено в SQL Server Hooke Online, раздел Deterministic and ffondciertninietie Functions (Детерминированные и недетерминированные функции). Второй ключевой момент заключается в том, что состояние сервера и все данные должны быть точно одними и теми же, когда вы выполняете операторы. Следовательно, любые изменения, которые простираются на объекты, не являющиеся внутренними для функции, типа возвращаемых результатов, данных или структур данных,- курсоров, которые не локальны для функции, или любая другая корректировка состояния системы, недопустимы. Чтобы проиллюстрировать это, давайте создадим функцию, которая обновляет все альбомы в наших таблицах на "the Beatles* и затем возвращает число величин, которые мы обновили.
CREATE FUNCTION albumSaetAHToTheBeatlee I ) RETURNS int М BEGIN DECLARE erowcount int :- UPDATE аШж . - SET artistld «-1 WHERE artiatld О 1 RETURN END При компиляции возникнет ошибка из-за оператора UPDATE: Server: Msfl 443, Level 16, State 2, Procedure aibumSsetAliToTheBeailes, Une 9 Illegal use of "UPDATE within a FUNCTION. (Сервер; сообщение 443, уровень 16, состояние 2. процедура album.fsetMlToTheBeatles, строка 9 Неправильное использование UPDATE внутри функции} Ограничения Как мы видели раньше, ограничения — ресурсы SQL Server, которые ’используются для автоматического обеспечения целостности данных. Одна иа основных причин исполыования ограничений вместо триггеров — это то, что оптимизатор з ал р особ может использовать их для оптимизации запросов. Например, если вы сообщаете SQL Server, что некоторая перемениая может иметь значения только между 5 и 10, и затем выполняете запрос для всех строк, которые имеют значение этой переменной большее, чем 100, оптимизатор будет знать деже без какого-либо просмотра данных, что нет никаких строк, которые удовлетворяют критерию. В SQL Server имеется пять различных видов ограяичеиийг □ NULL-ограничения — NULL-ограничения фактически не являются ограничениями, но обычна рассматриваются таковыми. □ Первичные ключи и уникальные ограничения — чтобы удостовериться, что наши ключи содержат только уникальные комбинации значений, □ Внешние ключи — используются, чтобы быть уверенными, что любые мигрирующие ключи имеют только подходящие значения, которые соответствуют ключам, на которые аяя ссылаются. Q Значения ио умолчанию — ограничения, связанные со значениями по умолчанию, используются, чтобы установить приемлемое значение но умолчанию для столбца. □ Проверка — ограничения, которые используются, чтобы ограничить значения, которые могут быть введены в отдельный столбец иля всю таблицу. - Так как первые три ограничения рассмотрены раиее в главе 10, здесь мы сосредоточим ваше внимание да последних двух.
Ограничения, связанные со значениями по умолчанию Я считаю, что значения по умолчанию являются очень важной частью защиты данных в том случае, если пользователь не знает, какое значение вводить в таблицу; значение может быть опущено, и установлено ограничение по умолчанию, определяющее подходящее предварительное значение. Хотя мы и можем предохранить пользователя от вводя неправильного значения, но не можем предотвратить его от ввода нелогичных значений. Например, предположим, что опытный пользователь SQL Server должен ввести значение’в столбец, называемый boogleFl, который является полем типа bit (boogleFl был выбран, потому что он означает для вас столько же, сколько некоторые из полей, которые имею» для нас смысл, значат для других пользователей), Не зная смысла boogleFl, во вынужденный создать новую запись, пользователь может выбрать значение 0, Использование значений во умолчанию дает пользователям пример вероятного значения для столбца. Нет никаких проблем при задании значений по умолчанию для полей и ято очень удобно для пользователя! Команда для добавления значения по умолчанию, имеет следующую структуру: I ALTER TABLE <имя_сяолОца> MD [ CONSTRAINT <имя_аначения_по_умолчанис> DEFAULT <конс’Еан1Ёнае__выражение> FOR <имя=столбц&> Мы будем использовать структуру обозначения, схожую с нашим предыдущим обозначением, рясп-дряя имя значения по умолчанию, используя следующий образец: Й£1с<имяитаблицн>$<имя=стелвца>5<об1»!й_«иа_данй1и<>8<оиисанив> ] Это приводит к следующему альтернативному синтакенсу: ALTER TABLE <галя_стол6ца> I ADD [ CONSTRAINT df 1х<имя_таблицы>$<»дая_сталвца>5<общий_тип__даниых>9<описанив> • DEFAULT <конствнтное_выражеиие> Ц FOR <имя_стол5иа> Конечно мы должны знать к данному моменту, что <имч__тавл;шы> и <*мя_столбца> определяют место заполнения, но что означают другие части? □ <оВщий_тип_данных> — Мы должны определить общий тип данных. Причиной этого является вопрос удобства. В нашем перечне общих объектов мы создали» группу определений ограничений, используемых по умолчанию, которые можем использовать неоднократно. Например, если бы мы определяли задание ограничения столбца типа int, равного 1, нам не потребовалось Вы другого ограничения, используемого по умолчанию, задаваемого для типа tinyint (в отличие от случая поля типа big int). О <описание> — Эго — общее описание цели значения по умолчанию, например, если ограничение используется, чтобы поддерживать все данные в столбце типа int больше нуля, мы могли бы называть это greaterThanzerc (больше, чем нуль). Это описание сделает имена значений по умолчанию более понятными. Не только это, но и когда рассматривается "нормализованный". многократного истользуемый пакет о общим типом данных, мы будем использовать определение значения по умолчанию, которое будет работать во всех ситуациях, где нам нужны одни и те же значения по умолчанию,
□ <конатактное_выражение> — скалярное выражение, которое могло би выть или литеральным значением, или NULL, или функцией. Мы рассмотрим несколько различных сценариев для константных выражений в следующем разделе. Литеральные значения по умолчанию Литерал — просто единственное значение того же самого типа данных, которое не требует никакой трансляции в SQL Server. Например, следующая таблица содержит примеры значений по умолчанию для нескольких типов данных: Тип данных Возможное значение по умолчанию int 1 varchar(10) 'Value' binary{2) 0x0000 datetime '12/31/2000' Так, в нашей ВД, скажем, мы хотим использовать по'умолчанию все альбомы "the Beaties", если пользователь не определяет другого; тогда мы могли бы сделать выбор из таблицы artist (артист), чтобы узнать, каково значение artistld (идентификатор артиста) для “the Beatles": | SELECT * FROM artist artistld name 1 the beetles 2 the who Таким образом, мы определяем следующее значение по умолчанию: I ALTER TABLE album ADD CONSTRAINT dfItAlbumSartistldSintegerSone DEFAULT (1) FOR artistld Небольшое предупреждение относительно литеральных значений по умолчанию. Лучше не давать литеральное значение по умолчанию столбцу, который является частью уникального индекса. Причина в том, что любой столбец, который имеет набор значений по умолчанию, вряд ли будет содержать уникальные значения. Аналогично и в любом индексе большое число повторений не будет удачным при функционировании. Функции Альтернативой жесткому кодированию значения по умолчанию является использование функции, чтобы получить значение по умолчанию. Давайте рассмотрим наш предыдущий пример, когда мы хотели установить артиста по умолчанию в "the Beatles", Чтобы сделать это, мы могли бы добавить к таблице artist поле defaultFl (флаг по умолчанию) co значением по умолчанию 0. Мы могли вы сделать это. если не хотим иметь только единственное значение, которое равно 1, плюс мы хотим обеспечить, чтобы пользователь мог вывиротъ запись по умолчанию.
I ALTER TABLE artist ADD eefaultEl bit I - NULL CONSTRAINT dfltArtistSdefaultFiSbooieanSfalse DEFAULT (0) Теперь установим строку “the Beatles" в 1: I UPDATE artist SET default?! ~ 1 WHERE name <• ’ the beetles* Таблица теперь включает поле, где пользователь может выбирать, какое требуется значение в качестве используемого по умолчанию; так что мы должны делать дальше? Для каждой версии SQL Server, вплоть до 2000, вы могли создавать запрос, который возвращает одну строку, подобную этой, содержащую единственное скалярное значение. Однако это сформирует сообщение об ошибке, так что для выполнения запроса мы создаем нашу собственную функцию: CREATE FUNCTION artistSreturnDeMulrld ( 1 RETURNS int WITH SCHEMABIKDIWG AS BEGIN DECLARE («artistld int SELECT eart-istld - artistld FROM dbo.artist WHERE default?! - 1 RETURN 0 r-:istla END GO ... и теперь, если мы выполним оператор: Ц SELECT dbo.artistSreturnDafaultld(J AS defaultvalue ... то получим: defaultvalue 1 Итак, для нашего решения мы можем записать следующий код: I ALTER TABLE album ADD CONSTRAINT dfItAlbwnSartistldSfunctibaSuseDefault DEFAULT dbo.artlstSreturnDefaultld(} FOR artistld
Теперь мы имеем точную функциональность, которую пытаемся получить из подзапроса, и, сделав это, создаем функцию, которую может-- использовать любой другой процесс. Чтобы проверить это, выполним следующий оператор INSERT, опустив поле ermstld: I INSERT INTO album(name) VALUES ('rubber -- rubber soul — "резиновая душа" SELECT album.мяе AS elbiwName, artist.name AS MtXstRame FROM album JOIN artist ON album.artistld - artist.artistld WHERE album.name - 'rubber soul' Результат, как можно видеть, содержит строку, которую мы создали: albumName artlstName rubber soul the beattes Ограничения по умолчанию в SQL Server 2000 — чрезвычайно мощное средство для задания начальных значений полям. Так как вы можете устанавливать значения по умолчанию в одной таблице из другой, есть несколько ограничений для их использования. Ограничения-проверки Ограничения-проверки используются, чтобы запретить ввод неподходящих даввьпс в наши таблицы. Они выполняются после ограничений по умолчание (так что вы ие можете задать значение по умолчанию, чтобы заменить неподходящее значеиие) и выполнения триггеров INSTEAD OF (вместо). Они не мог-— влиять яа сами значения, которые мы шдаямся поместить в таблицу, и используются лишь для того, чтобы проверить допустимость используемых значений. Имеются два разных типа ограничений-проверок: ограничение столбца и ограничение таблицы. Ограничение-проверка столбца относится к единственному столбцу и используется только тогда, когда в корректировке участвует отдельный столбец. Ограничение-проверка таблицы используется независимо от того, какие упоминаются столбцы. К счастью для нас мы не должны волноваться относительно объявления ограничения, является ли это ограничением столбца или ограничением таблицы. Когда. ЭДЬ, Server выполняет ограничение, ои анализирует, требуется ли проверять более одного столбца, и применяет соответствующее ограничение. В каждом из наших примеров ограничений мы обсудим, является ли это ограничением столбца или таблицы. Основной синтаксис оператора ALTER TABLE, который касается ограничений: ALTER TABLE <имя_таблицы> [WITH CHECK I WITH NOCHE-CKl ADD [CONSTRAINT сЬк<имк_та&ли1ж>$ (оа<я_сталвца>$<;ов®Л__тип_да»ивйх>81 <испапьзование> ] CHECK <условия_поиска> Часть синтаксиса здесь не завкома, поэтому будет объяснение новых частей. Когда вы создай» ограничение-проверку, задание WITH CHECK (используется по умолчанию) дает вам удобный случай решить, нужно или нет проверять существующие данные в таблице. WITH МОСНЕСК оставляет неправильные существующие данные, но не будет позволять новым значениям нарушать требования ограничения. Иногда задание WITH NQCRECK может быть необходимо, если вы добавляете стран ичение к существующей таблице, где нереально- итовершъ все существующие данные, но вы хотите проверять все последующие вводимые .данчые. Лучше избегать использования NOCHECK, если только это не является абсолютно яеоВхЬдимим.
В то время как понятно, иа что тылл^гегг <м*я_столвца>, то почему оно необязательно? Это ведь просто средство, чтобы включить-имя столбца, который вы пробуете защитить, в название ограничения. Одншю когда мы имеем дело с ограничением, работающим с несколькими столбцами, мы не должны включать имя столбца в название ограничения. Значение <обший_тип_данных> — просто описание класса типов дгенных, с которым могло бы работать ограничение. Когда это связано е ограничением-проверкой одного столбца, нет никакой причины, почему мы должны использовать различный код для varchar ЦО) или char (10) или даже char(100), если просто хотим гарантировать, что значение входной величины должно быть больше, чем пять символов. Вместо этого мы можем использовать ссылку на строку в нашем коде, чтобы обозначить вое эти типы данных. Это, конечно, невозможно в случае проверки таблицы. Значение «использование? будет в основном маленькой строкой текста, чтобы объяснить, для чего используется ограничение. Например, вели проверка должна выяснить, находится ли значение между нулем н десятью, мы поместим zeroThroughTen, Значение <условия_поиска> аналогично оператору WHERE обычного запроса SELECT без возможных подзапросов. Это может быть или системой доетуна, или ОПФ и использует имя или имена столбцов таблицы. Однако оно не может содержать обращение к любой другой таблице или к любой другой строке, кроме текущей строки. Велм обновляется несколько строк, ограничение имеет дело с каждой строкой индивидуально. Результатом части <условие_поиска> должно быть логическое выражение только е двуыятвозмпжшлми значениями TRUE или FALSE. Обычное ограничение-проверка, которое мы будем добавлять к большинству строк (varchar, char и т. д.), позволяет предотвратить ввод пустых даиных- Например, в - нашей таблице album мы имеем столбец name. Этот столбец не допускает значения NULL, так что пользователь должен ввести что-то, но что если будет введен одни символ пробела? INSERT INTO alht», (name, artistld) VALUES Если Вы мы позволили это в нашей БД, мы могли бы получить пустую запись, потому что возможна ситуация, когда пользователь введет такую строку, сделав ошибку в задании названия альбома. Во второй раз- ввод пробела вместо названия альбома приведет к появлению ошибки: Server: Msg 2627, Level 14, State 2, Une 1 Violation of UNIQUE KEY constraint ’ХАКа1Ьит_пате'. Cannot insert duplicate key in object 'album', The statement has been terminated. (Сервер: сообщение 2627, уровень 14, состояние 2, строка 1 Нарушение ограничения UNIQUE KEY (уникальный ключ) 'ХАКalbum._пат.е. Нельзя ввести двойной ключ в объект 'album. Оператор был удален.) Однако мы можем иметь иеуиикальиое ограничение на связанный столбец, подобно полю description (описание) или notes (примечание), где могли бы иметь много пустых вхождений. По агой причине мы могли бы добавить оввда-кицее ограничение, чтобы предотвратить повторное появление пустых столбцов:
I ALTER TABLE albwa WITH CHECK ADD CONSTRAINT chkAlbuffiSnaiaeSatrLngSnoEmptyStrlnii — урезание значения имени, добавляя звездочки; чтобы обратиться — к вначениям более безопасно, чем только проверка длины величины, — возвражаемой фукмиией RTRIM, которое дас» HULL, если название - HULL, — и единственный символ в провивиои случае. Это делает функция COALESCE. CHECK (( + COALESCE(RTRIM(name}, ' ') + '*4 <> '»*') Здесь RTRIM — функция, которая удаляет из аргумента завершающие пробелы. Окончательная функция CHECK здесь использует операцию COALESCE, чтобы обеспечить з случае, когда нет никаких символов, отличных от провела, возвращение строки нулевой длины, а если введено значение NULL, то это значение и возвращается. Конечно, мы предполагаем, что уже ввели значение, которое конфликтует е нашим ограничением, так что получим следующее сообщение об ошибке: Server: Msg 547, Level 18, State 1, Line 1 ALTER TABLE statement conflicted with COLUMN CHECK constraint 'chkWbum$nameSstring$noEmptySWng'. The conflict occured in database ’master*, table 'album', column 'name'. (Сервер; сообщение 547, уровень 16, состояние 1, строка 1 Оператор ALTER TABLE противоречит ограничению COLUMN CHECK 'chkAlbum$nanietstring$noEmptyString, Конфликт произошел в БД 'master. таблица 'alburn, столбец 'пате.) Если мы определим ограничение, используя WITH NOCHECK: I ALTER TABLE album WITH KOCHECK ADD CONSTRAINT chsAlbueSnaaeSetringSnoEBtpty 1 zrlr.g — урезание значения имени, добавляя звездочки, «робы обратиться - - к значениям более безопасно, чем солько проверка длины величины, — возвращаемой функцией RTRIM CHECK Ц + COALESCE(RTRIM(name}, ' ’} + ' *•) О '“'1 Оператор будет добавлен к определению таблицы, хотя NQCHECK означает, что неправильные значения останутся в таблице. Однако ирн любой корректировке, когда оператор обращается к полю, Судет запускаться ограничение-проверка. Далее вы пробуете установить одно из недопустимых значении в таблицу, которое вызовет ошибку. I UPDATE albw» SET name - name WHERE name = ' ’ Это даст сообщение об ошибке: Server: Msg 547, Level 16, State 1, Une 1 UPDATE statement conflicted with COLUMN CHECK constraint chkAlbumSnameSstringSnoEmptyString'. The conflict occured in database ’master', table ‘album’, column 'name'. The statement has been terminated. (Сервер: сообщение 547, уровень 18, состояние 1, строка 1 - Оператор UPDATE противоречит ограничению COLUMN CHECK 'chkAlbum$namefstnng$noEmptySirin^. Конфликт произошел в ВД 'master, таблица 'album, столбец 'пате. Оператор удален.)
В нашем следующем примере мы рассмотрим способы включения в ОПФ кода, чтобы выполнять и простые, и сложные проверки. В случае функции, которая проверяет пустые строки, мы можем создать нашу собственную ОПФ следующим образом: CREATE FUNCTION stringScheckForEmptyString ( SstringValue varchar (6000) 1 RETURNS bit AS BEGIN DECIARE в logicalValue.bit IF ('*' < COALESCE (RTRJNiestriogValuej, ' ' \ * '*'} « ’ SET @logicalValue = 1 ELSE SET eiogicalvalue » 0 RETURN (SlogicalValue END А затем сформируем следующее ограничение ALTER TABLE album. WITH NOCHECK ADD CONSTRAINT cijkAlbumSnaineSatringSneEiiiptyString — урезание значения имени, добавляя звездочки, чтобы с 5рат;:т= :я И -- к значениям Солее безопасно, чем только проверка длины величины, S — возвращаемой функцией RTRIM g| CHECK (dbo.string$checkForE»ptyString(паже! - Cl Теперь, если метод когда-либо изменится, это скрыто в пределах функции, и будет изменяться только в единственном месте. Большинство ограничений-проверок будет состоять из простой задачи проверки формата единственного поля. В следующих двух примерах мы будем использовать ОПФ, чтобы обесаечить общие функциональные возможности проверки дияпаяонов и сложные правила контроля, которые реализуют сложные правила и которые было бы трудно закодировать, используя непосредственно SQL-заирос, В первом примере мы собираемся добавить столбец е номером каталога к таблице album, учитывая, что номера каталога ~ алфавитно-цифровые значения следующего формата: <1ифра><цифра><цифра>- <цифра><иифра><буква_или_цифра><буква_или_цифра><ву1та_ипи_цифра>-<цифра><цифра> Примером такого номера каталога мог бы быть ’433-43ASD-33' с символами в строке, которые являются обязательными. Итак, мы определим ОПФ, называемую albumlcatalogNuntberValidate, которая будет включать проверку звачения catalogNuaber (номер каталога). Эта функция будет иметь две цели, а именно, проверку столбца в ограничении-проверке н проверку любого количества вставок строк и удалений данных. Наша функция возвратит значение тапа bit — 1, О или NULL для величины NULL, которые мы можем использовать для проверки данных. Первый шаг должен добавить наш новый столбец в БД.
I ALTER TABLE album ADD catalogNumber chai (12) NOT NULL — временное значение no умолчании, позволяющее созлаюь етолвец — NOT NULL при первоначальном создании CONSTRAINT tempDefaultAlbuniCatalogNmEber DEFAULT ('111-11111-11') — удаление его, потому что это - ие подходящее значение по умолчании ALTER TABLE album DROP wmpDefaultAlbumCatalogNumber Затем мы создадим нашу функцию, которая будет использоваться для продерки значений столбца album. catalogNumber: CREATE FUNCTION albmr.ScatalogNumherValxciace ( ficatalogKuosber char(12) i RETURNS bio AS BEGIN DECLARE eiogiealValue bit — проверка, удавле-гворяет ли значение установленной маске IF ecatalogNumber LIKE '[0-9][0-9][0-9]-[0-9][0-9][O-Sa-z][0-9a-«i[0-9a-z]-[0-9][0-9]' SET ilogicalValue - 1 -- да, удовлетворяет ELSE SET eioglcalValue -0 — нет, не удовлетворяет — заметим, что sua не можем просто написать RETURN 1 или RETURN 0, так 'Г- фуниния может иметь только одну точку вмхода RETURN eiagicalvalue END Проверим, работает яи ограничение. IIF dbo.albun»$catalogNumberValidate (' 433-llqww-33’ } - 1 SELECT 'Yes' AS result ELSE SELECT 'NO' AS result Этот оператор дает: result Yes Все, что осталось, — это построить функцию на основе ограничения-проверки: I ALTER TABLE album ADD CONSTRAINT chkAlburaScatalogNumberSfunctionSartlstScatalogNwiibexValidate — размещаем это прямо здесь, включая все неоВходамве для функции CHECK (dbo .albumScatalogNumbarValidate (catalogNumber] - 1)
... и проверить новое ограничение; мы видим, что следующий неправильный номер каталога дает системную ошибку: I UPDATE album SET catalogNumber = ’1’ WHERE number - 'the white album' что возвратит Server: Msg 547, Level IS, State 1, Line 1 UPDATE statement conflicted with COLUMN CHECK constraint 'chkAlbumJcatajogNwnberJfuncflanJartistScatalogNumbeiValldattf. The conflict occured in database 'master', table ’album’, column ’catatogNumber'. The statement has been terminated. (Сервер: сообщение 547, уровень I в, состояние I, строка 1 Оператор UPDATE конфликтует с ограничением COLUMN CHECK 'chkAIbumicatalogNumbertfunetion$artist$catalogNumberValidate. Конфликт произошел в БД 'master, таблице 'album, столбце 'eatalagN umber, Оператор удален.) Выполнение его с правильными величинами будет вполне успешным: UPDATE album SET catatogNumber - ’433-43ASD-33' WHERE name = 'the white album' В качестве второго примера добавим столбец к твблицеartist, который будет содержать маску LIKE. Обратите внимание, что требуется маска значительно больше, чем размер фактического столбца, так как некоторые из возможных масок используют несколько символов, чтобы указать единственный символ. Мы должны также обратить внимание, что это — varchar, так как использование переменных типа char проблематично в маске LIKE из-за добавления пробелов. ALTER TABLE artist ADD eatalogNumberMask varchar(100) NOT NULL И -= здесь приемлемо иметь маску в виде процента CONSTRAINT dfltArtistScatalogNumberMaskSstringSpercent DEFAULT ('%') Затем сформируем функцию, которая будет осуществлять доступ к столбцу, чтобы проверить, что наше значение соответствует маске: I ALTER FUNCTION albumScatalogNumberValidate ( ScatalogNuttber char(12), Sartistld int — теперь основываемся на поле artistld ) RETURN’S bit AS BEGIN DECLARE tlogicalValue bit, CatalotjNumberMask varchar(100)
ISLLEC? ecatalogHumberMasк = catalogNumberMask - FROM artist WHS® artistld - tartistld IF ScatalogNueber like gcatalogMumberMask FEI ?L;.jtcslValue - 1 ELSE SET eiogicalValue - 0 ' ' RETOE®' eiogicalValue EHD GO Теперь дадим столбцу artist правильные маски: UPDATE artist SET catalogNuinberMas к ~ • [0-9] [0-9] Г0-9]-[0-91 [0-91 [0-9a-z] [0-9a-z] (O-Sa-zJ-[0-9] [0-9V WHERE artistld =1 -- Бигле UPDATE artist SET catalogNumberMask = ' [а-г][a-z][0-9]£0-9][0-9][0-9][0-9](0-91(0-91 EO-9][0-9][0-9]' | WHERE artistld -2 — Xy и протестируем работу функцда следующим образам: IXF dbo.aibumScatalogSumberValidatei*443-43ASD-33' , 11 — 1 SELECT 'Yes' AS result ELSE SELECT ’No' AS result IF dbo.albufnScatalogrltuinberValidate (’aallllllllll', 2} = 1 SELECT ’Yes' AS result ELSE SELECT 'MO' AS result GO Теперь добавим ограничение к таблице: И ALTER TABLE albuia. ADD C0KSTRA™ chkAlbumSca- s.-ogHumberS functionSar 11 s tS catalogMwrfcerValidatB Щ CHECK (dbo.albumScatalogKumberValidateCcatalogNuniber, artistld) ~ 1) Однако няиш данные не подходят: Server Msg 547, Level 16, State 1, Une 1 ALTER TABLE statement conflicted with TABLE CHECK constraint ’chtaMbumScatalogNumberSfijnctlonSartjstScataiogNumberValidaW. The conflict occured In database 'master', table ‘album'. (Сервер.- еаобщенш S47, уровень 1 в, состояние 1, строка 1 Оператор ALTER TABLE конфликтует с ограничением TABLE CHECK 'chkAlbumicatalogNumbertfunetiontartisttcatalopNumbeiVaUdate^ Конфликт, произошел в ВД 'master, таблице 'album',)
Мы могли бы использовать вариант NOCHECK, тан как его будет означать, что любые существующие данные могут быть неподходящими, когда мы вводим ограничение, в то время как любые новые данные будут автоматически проверяться. Одвако в этом примере есть проблема, связанная с там, что ограничение не защитит пользователя при изменении маски. Если это существенно, мы могли вы реализовать таблицу, основанную иа времени, где можно установить, что маска номера каталога будет иметь некоторое значение для даты публикации в период с 1 января 1970 года по 31 декабря ЮТБ года и другое значение в период с 1 января 199в года по настоящее время, В нашем примере мы не будем использовать параметр NOCHECK, только для того, чтобы . показать вам, как легко найти отклонения в данных. Следующий запрос выявит проблемы в таблице: — чтобы найти, где наии дашиае не соответствуют ограничению, ~= мы запускаем следующий запрос: SELECT dbo.albmnScatalogSuBberValidate (catalogNumber, artistld) AS validCatalogNumber, artistld, name, catalog»umber FROM album WHERE dbo.albumScatalogNumberValidate(satalogHumber, artistld) о 1 Он дает нам список строк с неправильными данными. Используя подобный код для наших предыдущих примеров, мы можем исправить ошибки и затем добавить ограничение. Попытка добавить неправильное значение: I UPDATE album SET catalo^Number * *1* WHERE name = f the white album* возвратит ошибку Server Msg 547, Level 16, State 1, Line 1 UPDATE statement conflicted with COLUMN CHECK consttaint 'chkAJbumScatatogNumberSfunctionSartistSDatalogNumberValidate', The conflict occured in database ’master', table 'album', column ’catatogNumber'. The statement has been terminated, (Сервер: сообщение 547, уровень 1в, состояние I, строка 1 Оператор UPDATE конфликтует с ограничением COLUMN CHECK 'cMAibumfcatalogNumberfifunction$arti8t-featalogNumberVaUdate, Конфликт произошел в БД 'master', таблице 'album , столбец 'eatalogNumber. Оператор удален.) Теперь мы можем попытаться скорректировать оператор: UPDATE album I SET catalogNumber - ’413-43ASD-33' Ц WHERE name = 'the white album' — артист - Витлэ Используя такие подходы, мы можем формировать код проверки любой отдельной строки для наших таблиц.
Однако в этом способе кодирования имеется недостаток. Как мы сказали иретаауитально. каждая ОПФ будет выполняться каждый раз для каждой строки и каждого столбца, которые были изменены при корректировке, а выполнение многократных проверок может привести к ухудшению работы, Альтернативный способ, который мы представим позже, состоит в том, чтобы использовать триггер для проверки всех изменений таблицы в единственном запросе. Несмотря на недостаток, этот способ решения проблемы проверки строки очень прост , понятен и легок в обслуживании. Однако если наши ограничения-проверки сложны, требуют доступ ко многим табладам, то функционирование может оказаться неаффективным и использование триггера будет более предпочтительно. Обработка ошибок, вызванных ограничениями Обработка ошибок — одна из наиболее важных частей написания любого оператора, изменяющего данные в SQL Server. Всякий раз, когда оператором нарушаются требования ограничения, как мы видали, формируется сообщение об ошибке. SQL Server обеспечивает нас простейшими средствами обработки ошибок, чтобы определить, что должно произойти в операторе, и мы здесь их рассмотрим. На протяжении этой главы мы выполняли операторы, чтобы доказать, что ограннкения выполняются. Когда ограничения нарушаются, мы получаем сообщения об ошибке, подобные следующему: Server: Msg 547, Level 16, State 1, Une 1 UPDATE statement conflicted with COLUMN CHECK constrain 'cnKAIbumScatetogNumberSfunctionSanistScatalogNurnberValidate', The conflict occured in database 'master', table 'album', column ’catalogNumber'. The statement has been terminated. (Сервер: еооЛцвнив 5-17, уровень 16, состояние 1, строка 1 Оператор UPDATE конфликтует, с ограничением COLUMN CHECK 'chkAlbumfcatalogNumberffuncttonfarttsttcatalogtlumberVaMate. Конфликт произошел в ВД 'matter, таблице 'album. столбец 'catalogNumber'. Оператор увален.) Это сообщение об ошибке, которое видат анализатор запроса (Query Analyser) и которое может видеть приложение пользователя. Сообщение об ошибке состоит иа нескольких основных частей: □ Код ошибки (Error number), Msg 547 — код ошибки, который возвращается к вызывающей программе. В некоторых случаях этот код ошибки существенен; однако в большинстве случаев достаточно просто знать, что он отличен от Нуля. □ Уровень (level), Level 16 — уровень серьезности сообщения. 018 обычно рассматриваются как сообщения пользователю, где 16 является значением по умолчании. Уровни 15У25 — действительно серьезные ошибки, которые приводят к разрыву связи (с сообщением, записываемым в протокол) и обычно связаны с порчей данных. □ Состояние (State), State 1 — значение от 1 до 127, которое характеризует состояние процесса, когда возникла ошибка. Всегда равняется 1, если это не является частью оператора RAISERROR, □ Строка (Line), Une 1 — строка в групповой операции или объекте, где возникла; ошибка. Очень полезна для целей отладки, □ Текст ошибки (Error Text) — текстовое объяснение возникшей ошибки.
Однако в наших SQL-операторах мы имеем доступ только к коду ошибки. Сделать это легко, используя глобальную переменную 3Serror. Ц SET В<имя_переменной_см^§ш> s Sierrar Таким образом мы получаем значение кода ошибки любого сообщения, которое должно появиться в ответ иа предыдущий оператор. Это значение будет: 0 Нет ошибки. 1+49999 Системные ошибки либо из-за ограничений, либо из-за действительных ошибок системы, которые не могут нами контролироваться. Примеры таких системных ошибок включают синтаксические ошибки, дисковые ошибки, неправильные SQL-операции и т, д. М000 Нврегламентиреванные сообщения об ошибках без заданного кода или любые общие сообщения, когда операции пользователя не нуждаются в выделении их из любых других сообщений. 50001 и больше Определяемые пользователем сообщения. Если клиентская система может использовать код ошибки, то неплохо разработать общий или персональный стандарт для кодов ошибок. Мы рассмотрим схемы обработки ошибок в главе 42. Максимальный код ошибки определятся макеамалышм значением типа данных bigtnt.. Когда выполняются любые операторы INSERT, UPDATE или DELETE з коде нашей программы, триггера или хранимой процедуры, мы должны всегда проверять состояние кода ошибки команды. Обратите внимание, что это значение изменяется с каждой командой в вашей БД, так что любой код после каждого оператора корректировки должен получать значение, находящееся в Меггог. (Вы также можете получить значение параметра Mrowcount (который определяет- число скорректированных строк последней операцией) после оператора SET, поскольку этот оператор не воздействует на значение esrowcount.) Что делать со значением ошибки, зависит от ситуации. Обычно лучите выполнять каждый оператор, который изменяет данные, внутри своей собственной транзакции, если Вы не выполняете единственный оператор. В следующем примере кода мы попробуем обновить альбом с неправильным номером каталога. Это вызовет ошибку, которую мы обработаем, сформировав оператор RAISBRROR, который сообщит пользователю, что произошло. Каждый раз, когда мы снова будем выполнять команду UPDATE, то будем проверять, равна ли переменная йвеггог нулю, в если нет, будем знать, что команда-не выполнена: I BEGIN TRANSACTION DECLARE (serrorHold int UPDATE album SET cata1ogNuaher = ' 1' WHERE name - 'the white album* SET SertotHold - йвеггог
IF fierrerHald о fl BEGIN RAISERHQR 5GOOO 'ОкмВка в корректировке первого номера каталога в табгмце album' ROLLBACK TRANSACTION END IF gerrorHold - 0 BEGIN UPDATE album SET catalogNumber ~ '1* WHERE пав» = ' the white album' SET SerrorHold = Merror IF gerrcrHold о 0 BEGIN RAISERBCR 50000 'Ошибка а корректировке второго номера каталога' ROLLBACK TRANSACTION END END IF eerrwHold « 0 COWHT TRANSACTION ELSE RAISERROR 50000 'Программа обновления не выполнена' Здесь формируется транзакция, еде сначала объявляется переменная веггаШои (размещение ошибки) типа int. Далее в таблице album полю aatalogltimbar записи, где поле палю имеет значение ‘the white album1, задается значение 'Г. После »той операции проверяется глобальная переменная terror и ее значение записывается в переменную GerrcrHold. Если это значение не равно нулю (произошла ошибка), формируется сообщение об ошибке с номером 50000 и организуется откат транзакции к началу. Если же ошибки не было, повторно выполняются те же действия. Наконец, в завершение, если значение переменной gerrorHoldравняется нулю, транзакция успешно завершается, в противном случае формируется сообщение об ошибке е кодом 50000, Выполняя ату нрограмму, мы получим три ошибки: Server: Msg 547, Level 16, State 1, Line 1 UPDATE statement conflicted with TABLE CHECK constraint 'chkAlbumScatatogMumberSfunctionSartistScatalogNumberValtdate'. The conflict occurea in database ’master*, table 'album*. The statement has been terminated. Server: Msg 50000, Level 16, State 1 Line 12 Ошибка в корректировке первого номера каталога в таблице album Server; Msg 50000 Level 16, State 1. Une 33 Программа обновления не выполнена (Сервер: сообщение 547, уровень 16, состояние I, строка I Оператор UPDATE конфликтует с ограничением TABLE CHECK 'ehkAlbumfcatatogltumber$function$artist$catalogNumberVaUdati. Конфликт произошел в БД ‘master, таблице ’album'. Оператор удален.
Сервер; сообщение 50000, уровень 1, состояние 1, строка 12 Ошибка в корректировке первого номера каталога в таблице album Сервер: сообщение 50000, уровень I, состояние 1, строка 33 Программа обновления не выполнена.) При кодировании дополнительных сообщений об ошибках мы можем создать последовательности сообщений, которые точно нам скажут, где в программе произошла ошибка, что позволит легче выполнить отладку. Триггеры Триггеры представляют собой тип хранимых процедур, связанных с таблицей или представлением и выполняемых только тогда, когда содержимое таблицы изменяется. Они могут использоваться, чтобы обеспечить любые бизнес-правида, и особенно полезны в ситуациях, которые слишком сложны для управления ограничениями-проверками. Мы будем рассматривать триггеры как средства выполнения следующих операций: □ обеетечивать ссылочную целостность между БД; □ проверять-межстрочные ограничения внутри таблицы; □ проверять межтабличные ограничения; □ вводить желаемые побочные аффекты в наши запросы. Главное преимущество триггеров над ограничениями -— возможность непосредственного доступа к другим таблицам и работы е несколькими строками одновременно. В -триггере мы можем использовать почти все команда Т-SQL за исключением следующих. ALTER DATABASE- DROP DATABASE- RESTORE LOG RECONFIGURE RESTORE DATABASE Можно иметь триггеры, которые запускают INSERT, UPDATE или DELETE или любую их комбинацию. Можно также иметь для таблицы несколько триггеров, различных или одинаковых типов. Небольшое предупреждение: никогда не включайте операторы, которые в триггере возвращают наборы результатов, если они не допустимы во всех случаях, где триггер может быть запущен. Имеются две различных модели триггера: □ INSTEAD OF (вместо) — Означает, что триггер работает вместо команды (INSERT, UPDATE или DELETE), воздействуя на таблицу или представление. В этом случае мы можем делать с данными все, что захотим, или обновить их там, где они находятся, или переместить их в другое место. Мы можем иметь по отдельному триггеру этого типа для операций INSERT, UPDATE и DELETE на таблицу. Однако вы можете и объединить все три в один и иметь единственный триггер, который запускается для всех трех действий.
□ after (после) — Означает, что триггер запускается после команды, которая воздействовала на таблицу, но не иа представления. Триггеры AFTER используются для задания обработчиков расширенных бнзнес-правил, и вы обычно будете помещать них различные виды механизмов регистрации. Вы можете иметь неограниченное число триггеров APTER, которые запускаются операциями INSERT, UPDATE и DELETE или любой их комбинацией. Даже если мы имеем триггер INSTEAD OF, мы все же можем иметь столько триггеров АРТЕМ, сколько потребуется, так как все они могут выть объединены в общий триггер. Кодирование триггеров Следующая структура содержит основной синтаксис триггера: CREATE TRIGGER <имя_тавлииы$т*т_триггвраВтип_испольэоваяия> Он <имя_тавлицм> { <*«,ч_првди’авления> INSTEAD OF | AFTER [DELETE] t, ] [INSEM] [, HURDME] Я AS Ц <SQL операторы> Следующие пункты рассматривают различные части оператора CREATE TRIGGER, которые нуждаются н дополнительном разъяснении. □ CREATE TRIGGER <имя_тавлииы5тип_триггера5тип_исяользования> . Имя триггера не очень уж важно, поскольку вы не будете вызывать его явно из кода. Я обычно люблю давать им названия, похожие иа названия всех других объектов, которые мы рассматривали ранее, хотя, как и с любым объектом, который мы создавали, выбор имени в полной мере зависит от -личного вкуса. □ INSTEAD OF | AFTER — сообщает триггеру, когда он должен выть запущен, как было отмечено выше. □ I DELETE] [, ] [INSERT] [,] [UPDATE] — МЫ МОЖвМ ЗвдаТЬ ОДИН ИЛИ НвСКОЛЬКО ИЗ ' этих элементов, чтобы сообщить нашему триггеру, когда следует запускаться; например, задав В AFTER DELETE, INSERT ... будет сообщено триггеру, что он должен запускаться после операторов DELETE и INSERT, но ие UPDATE. Задание f| INSTEAD OF INSERT, UPDATE ... дает нам триггер, который запускается вместо INSERT и UPDATE, но DELETE будет работать как обычно. □ <SQL_onepaTopM> — любые SQL-операторы. Остальная часть этого раздела будет посвящена тому, какие операторы помещать в триггер. Мы рассмотрим □ доступ к измененным строкам информации; □ определение, какие столбцы выли изменены; □ организацию контроля нескольких отрок одновременно;
□ создание нескольких триггеров для одной и той же операции (INSERT, UPDATE, DELETE); □ обработку ошибок. Доступ к измененным строкам Основное достоинство кодирования триггеров заключается в том, что независимо от того, сколько строк изменяется, мы получаем доступ ко всем строкам. Мы обращаемся к этим значениям через две таблицы, которые позволяют нам видеть состояние наших данных до и после корректировки. Эти таблицы называются DELETED (удаленные) и INSERTED (добавленные) и существуют они в намята только в период работы триггера и недоступны вне этих границ. Таблица INSERTED будет заполнена во время выполнения операций INSERT или update, а таблица DELETED заполняется во время операций UPDATE или DELETE. Операции INSER” и DELETE понятны, но обработка в SQL Server операции UPDATE менее очевидна. Вее операции UPDATE в SQL Server логически делятся на операции DELETE я INSERT, удаляя строки, которые должны быть изменены, а затем вставляя их в новом формате. , В следующем примере мы создадим триггер, чтобы показать содержание таблиц INSERTED и DELETED после операции UPDATE: — создание триггера, который возвраиает в качестве результата набор как — введенных, так и ysa~eHHt:x злэкгитсв таблицы только в демонстрационных — целях. Для реальных триггеров это плохая идея CREATE TRIGGER artistSaEcerUpdateSshawInsestedAndDeleted OK artist AFTER UPDATE — запускается после выполнения UPDATE AS SELECT 'содержимое таблицы INSERTED' — ииформационмъш вывод SELECT * FROM INSERTED SELECT 'содержимое таблицы DELETED' — информационный вывод SELECT * FROM DELETED GO Затем для таблицы запустим оператор UPDATE, который будет устанавливать у всех имен заглавные буквы как пример различий между содержимым таблиц INSERTED и UPDATED: UPDATE artist SET name = UPPER(паке) Этот оператор возвращает следующие четыре результата; содержимое таблицы INSERTED artistld name ... 1 2 THE BEATLES . THE WHO
содержимое таблицы DELETED artistld name ... 1 the beatles 2 the who Определение, какие столбцы были модифицированы Имеются две функции, которые мы можем использовать в наших триггерах, чтобы определить, какие столбцы были использованы в операциях INSERT или UPDATE, ио заметьте, что ист эквивалента для операции DELETE. Мы будем это использовать, чтобы избежать проверки данных, которые не затронуты в операции, и, следовательно, увеличим производительность наших триггеров. Конкретно, функции получают информацию из: □ перечня столбцов в операторе INSERT: 8| INSERT tableBame («столбец 1>, «столбец 2>, ... , «столбец S>) □ выражения SET оператора UPDATE: I UPDATE tableNamo SET «столбец 1> - «значение 1>, «столбец 2> — «значение 2>, «столбец N> «значение N> Первый метод определения, что столбец был обновлен — функция update (<имя столвна>), Она возвратит логическое значение, которое сообщает нам, был ли столбец упомянут в операторе корректировки. Например, следующий код будет проверять, были ли столбцы name или description модифицированы: IF update(name) OR update(description) BEGIN <80Ь-операторы> END В качестве примера создадим следующую таблицу: CREATE TAB1E coluninUpdateTest ( ColumnUpdatedTestld int IDENTITY, column! int, colwcnZ int, coluranS int — битозая маска, значение 1 — битовая маек®, значение 2 — битовая маска, значение 4 — битовая маска, значение 8 Здесь создается таблица coluimUpdatedTest (тестирование столбцов с помощью функции update), состоящая из четырех столбцов типа int, первый из которых является автоинкрементным.
Далее мы можем определить следующий триггер и добавить ег CREATE TRIGGER teetlt ON columnUpdatedTest AFTER INSERT, UPDATE AS IF update (columnUpdated"estI<i) BEGIN SELECT ’ coluBviUpdatedTestld END IF update (coliunnl) BEGIN SELECT '-column! модифицирован' END IF update(column2) BEGIN SELECT 'column2 модифицирован' END IF update(columnsI BEGIN SELECT 'columns модифицирован' END GO Теперь, когда мы создадим новую запись: будут возвращены следующие строки oolumnUpdatedTest модифицирован column 1 модифицирован column2 модифицирован column3 модифицирован Если мы модифицируем один столбец: UPDATE columnUpdatedTest SET colujnni - 2 where colunurUpdacesiTestZd - 1 тот же самый триггер ироинформирует нас: columns модифицирован
Другой метод, который стоит упомянуть, на самом деле не столь употребителен. Это — функция columna_updatedt}. Выполняя функцию cclumns_upd&te<i О, мы вернем шестнадцатеричное целое число с битовой маской столбцов в таблице. Например, в созданной нами таблице мы имеем следующие столбцы: coiumnUpdatedTestld -- битовая маска, значение 1 columnl - — битовая маска, значение 2 column2 -• битовая маска, значение 4 columns , — битовая маска, значение 8 Битовая маска для всех столбцов в таблице будет: 1 * 2 -Н + 8 = 15. Итак, в предыдущем триггере мы можем использовать columns_updated () вместо update О , чтобы получить битовую маску столбцов, которые выли обловлены, Например, если мы введем все столбцы в операторе insert: I INSERT columnOpdatedTest (columnl, columnl, column31 VALUES (1, 1, 1) ... и выполним функцию columns_apdated () в подключенном триггере, то получим следующую битовую маску: OxOF ... которая равна 15, или битовую маску для всей таблицы, как мы показали ранее. Если мы используем следующий код: I UPDATE columr.UpdatedTeat SET columns - 2 : WHERE coiumnUpdatedTestld - 1 ... то получим битовую маску для этого единственного столбца, который мы изменили, и никаких других, которые ие были в операторе: 0x04 Итак, мы можем протестировать эту величину, чтобы посмотреть, какие столбцы были обновлены, используя побитовые операции о помощью кода наподобие следующего: ilF columns_updated О 4 4 = 4 — третий столбец s таблице жа — модафицироааи IF со1-штм_updated 11 4 15 - 15 — первые чем стоябца были — иояивицироав»ш Что плохого в использовании функции columns_updated () 1 fffino з том, что код, написанный с использованием этой функции, должен измениться, если изменится порядок наших столбцов. Это делает наш код непредсказуемым, поскольку мы должны проверить кол триггера» вели добавляем столбец в любую позицию таблицы, кроме последней. Функция update () намного более предпочтительна для проверки изменяемых столбцов. Функция columns_updatedO здесь показана для полноты рассмотрения.
Написание межстрочных проверок Тах как триггеры запускаются один раз для многострочных операций, операторы внутр:: триггера должны быть закодированы е учетом работы с несколькими строками. Довольно легко написать триггеры, которые предназначены только для корректировки одной строки; однако каждая часть кода, который вы пашете, должна будет обычно вместо одной просматривать строки, которые не удовлетворяют вашим критериям. Вели вы не хотите заставлять пользователей вводить только одну строку аа рае, то доляшы закодировать ваши триггеры таким образом, который допускает, что может Сыть модифицировано больше одной строки в таблице. В следующем примере мы- построили триггер с двумя очень похожими средствами проверки, и хотя оба работают е одинаковыми строками, только одао будет обнаруживать многострочные неприятности. GREATS TRIGGER artist$a£terC;pdateSdemoMultiRow ON artist AFTER INSERT, UPDATE -- запускается после добавления и обновления AS IF NOT EXISTS (SELECT « FROM INSERTED WHERE name ('the who', 'the beatles', 'Jethro toll') J BEGIN SELECT 'Неправильная первая проверка артиста' END IF EXISTS (SELECT * FROM INSERTED WHERE name NOT IN ('the who', 'the beetles’, 'Jethro tull'J ) BEGIN SELECT 'Неправильная вторая проверка артиста' END GO Итак, мы написали наш триггер и хотим проверить его. Так как мы собираемся писать запрос, который будет работать правильно, поместим наш оператор INSERT между операторами BEGIN TRANSACTION и ROLL-BACK TRANSACTION, чтобы иметь возможность проверять оператор, одновременно предотвращая любую корректировку наших данных от фактического размещения в таблице. Триггер, который мы натесали — только для демонстрации, а в разделах, описывающих триггеры AFTER и INSTEAD OF, мы рассмотрим возможные способы фактического возвращения ошибки из триггеров. I BEGIN TRANSACTION INSERT INTO artist (name, detaultFL, catalogNumberMask} VALUES ('ROLLING STONES', 0, ' V} ROLLBACK TRANSACTION -- отмеюгть проверку строк Этот запрос возвратит следующее: Неправильная первая проверка артиста Неправильная вторая проверка артиста
потому что в обеих ваших проверках IIF МОТ EXISTS (SELECT * FROM INSERTED- WHERE name IS ('the who7, 'the beetles’, '-eehro tall’)) и IF EXISTS (SELECT * FROM INSERTED | WHERE name NQT IN ('the who', * the beetlesг rjethra tull*)) ... 'rolling STONES^ не попадает внутрь границ, В первом случае имени нет в ipynne, так что нет никаких строк, которые удовлетворяют тесту NOT EXISTS. Во втором случае имени таюке нет в группе, так что все строки возвращаются запросом, и строка существует. Далее мы попробуем использовать многострочный тест. Лучший способ выполнить многострочный тест — использовать операцию UNION: I SELECT 'ROLLING STONES', 0, '%' UNION SELECT 'JETHRO TOLL7, 0, '%' которая возвращает JETHRO TULL 0 % ROLLING STONES 0 % Выполняя такие объединения (в этом случае один оператор дает подходящий результат, а другой — нет), мы можем проверять многострочные возможности наших триггеров; BEGIN TRANSACTION В INSERT INTO artist (name, defaultFl, catalogNumberMaakl SELECT 'ROLLING STONES', 0, '%' UNIC® S SELECT 'JETHRO TOLL7, 0, ’ %' g ROLLBACK TRANSACTION — отменить проверку строк На этот раз только одна из наших проверок не удается: Неправильная вторая проверка артиста Нам следует разовраться, почему это происходит, В следующей проверке триггер вообще не пошлет вам сообщение: IIP NOT EXISTS (SELECT * J- FROM INSERTED WHERE name IN ('the who', 'the beetles', 'Jethro tull7)) зэз
Так как мы имеем одну строку, которая удовлетворяет критерию (строкаJethro tullj, только у нее INSERTED, пата попадает в нашу испытательную группу. Так что Должна быть возвращена единственная строка, и такая строка существует. Это — довольно общая проблема при написании триггеров. Даже после десяти лет создания триггеров я должен проверять мои триггеры, чтобы удостовериться, что не попал в эту западню. Конечно, аучше всего проверить каждый из ваших фрагментов кода подобным образом, чтобы удостовериться, что он может обращаться с любыми данными, помещенными в наго. Обратите также внимание, что мы удалим триггер artist-5afterUpdateSdeieoMultiRow из вашей БД, поскольку он больше не потребуется: Ц DROP TRIGGER artistSaftfeiUpdateSder.oMultiRaw Использование нескольких триггеров для одной оперении Одна свежая особенность SQL Server MOO — возможность иметь несколько триггеров AFTER для различных контролирующих операций, Это позволяет нам строить триггеры, которые являются очень специфичными по природе, как в следующих вариантах: □ Вариант вложенных триггеров — глобальная настройка сервера воздействует через: sp__configure 'nested, triggers', 1 ! О - где 1 - включить, 0 — выключить Если операция каскадирования включена, то запускаются триггеры мех используемых таблиц. Имеется предел в 32 вложенные операции триггеров. Обратите внимание, что мы можем создавать неограниченные циклы триггеров, которые будут каскадировать операции одной таблицы, и-если происходит любая корректировка исходной таблицы, то его начнет процесс снова. Вложенные триггеры установлены на уровне сервера. □ Вариант рекурсивных триггеров — настройка для конкретной ВД воздействует через: sp_dbcption ' <имя БД>*, 'recursive triggers', 'TRUE' I 'FALSE' Данный-вариант заставляет триггеры заново запуститься, когда триггер выполняет корректировки в таблице, к которой он подключен. Это требует, чтобы был установлен вариант вложенных триггеров и он также устанавливается на уровне ВД. В следующем примере мы демонстрируем, как работают настройки вложенных и рекурсивных триггеров. Сначала мы создаем две очень простых таблицы, в которые сможем поместить одно и то же значение: I CREATE TABLE tableA ( field varchar(46) NOT NULL ) CREATE TABLE tableB ( field varchar(40) MOT NULL ) GO Здесь создаются dee таблицы, tableA и tableB, содержащие no одному полю field (поле) типа varchar (40(, которые не могут принимать значение MLLE.
...и затем два очень простых триггера, каждый иа которых помещает те же величины в другую таблицу: CREATE TRIGGER tableASaftertJpdateSdemoNestReciirse ON table* AFTER INSERT AS — сообщение пользователю, что триггер запушен SELECT 'триггер добавления в tableA' — помещение значений в tableB INSERT INTO tables (field) SELECT field FROM INSERTED GO CREATE TRIGGER tableBSafterUpdateSdemoNeetRecurse ON tables AFTER INSERT AS — сообшенив пользователю, что триггер запущен SELECT ’триггер добавления в tableB’ — помещение значений в tebleA INSERT INTO tableA (field) SELECT field FROM INSERTED GO Теперь выполним ими установки — выключим вложенные триггеры для сервера: IEXEC Bp_configure 'nested triggers', 0 — (1 - включен, С - выключен) RECONFIGURE — требуется для немедленной конфигурации сервера Далее мы введем строку в tableA: insert INTO tableA (field) Ц VALUES (’пробная величина’) и запустим только триггер таблицы tableA: триггер добавления в tableA Вели мы включим вложенность триггеров и сделаем так, чтобы рекурсивные триггеры для нашей испытательной ВД были выключены: IEXEC sp_configura 'nested triggers', 1 — где 1 - включена, О - аьжлючеяо RECONFIGURE EXEC sp_dboption 'bookTest', 'recursive triggers', 'FALSE'
тогда мы можем выполнить тот же код для таблицы tableA: INSERT ОТО tableA (field) _ VALVES ('пробная величина'} В этом случае мы получим нечто другое: триггер добавления в tableA триггер добавления в tableB триггер добавления в tableA триггер добавления в table В Server: Msg 217, Level 16, State 1, Procedure tableBSafterUpdateSdemoNestRecurse, Line 7 Maximum stored procedure, function or trigger nesting level exceeded (limit 32), (Сервер: сообщение 217, уровень 16, состояние 1, процедура tableB$afterUpdate$demoNeatRecurse, строка 7 Максимальный уровень итерации вложенной процедуры, функции или примера превышен (предел 32).) Это — в значительной степени то, что мы могли Вы ожидать. Правильная ситуация с рекурсивным триггером происходит только тогда, когда триггер девствует на ту же самую таблицу, на которой он установлен. Чтобы проиллюстрировать это, лют, изменим триггер таблицы tableA так, чтобы вставлять строку в таблицу tableA, а не в tableB: ALTER TRIGGER tableASafterUpdateSdetsoNestRecurse ON tableA AFTER INSERT AS — сообщение пользователе, что триггер запущен SELECT 'триггер добавления в tableA' -- помещение значений в tableA INSERT INTO tableA {field} SELECT field FROM INSERTED GO Когда мы введем одну строку: I INSERT INTO tableA (field) VALUES ('пробная величина*)
оператор потерпит неудачу н откатит назад транзакцию. Это иллюстрирует, что рекурсивные триггеры могут использоваться, если очень внимательно кодировать триггеры, чтобы они не могли запускаться во лее 32 раз: триггер добавления в tableA триггер добавления в tableA Server: Msg 217, Level 18, State 1, Procedure tabieASafterUpdateSdernoNestRecurse, Line 7 Maximum stored procedure, function or trigger nesting level exceeded (limit 32). (Сервер; сообщение BI 7, уровень 16, состояние I, процедура tableASafterUpiateidanoNeatRecune, строка 7 Максимальный уровень итерации вложенной процедуры, функции или триггера, превышен ( предел 32).} Обработка ошибок Имеются два способа реализации обработки ошибок, испольауя триггер* Или код обработки ошибок помещается в триггер, или он содержится внутри вызывающего кода клиента. Обработка ошибок внутри триггера В разделе, посвященном ограничениям, мы видели, что должны проверять состояния ошибки после каждого SQL-oneparopa, который изменяет данные. Печально, но такая обработка ошибок не дает нам возможность получать коды ошибок от триггеров по двум причинам: □ Выполнение отката транзакции в триггере откатывает транзакцию и останавливает все выполнения продолжающихся операторов — Затруднение атой проблемы в том, что нет абсолютно никакого способа сообщить всем триггерам, что текущая операция потерпела неудачу и что только единственную операцию можно откатить. Пусть вы имеете несколько триггеров, работающих последовательно, и вы вложили их в транзакции. Если заключительная транзакция терпит неудачу, то группа откатывается независимо от успеха предыдущих транзакций. Вы не мажете откатать только неудавшуюся транзакцию и продолжать работать с успешными. □ Если обнаружена серьезная ошибка, вея транзакция автоматически откатывается — Определение серьезной ошибки весьма произвольно. Оно, одна ко, включает нарушения ограничений, так что любые корректировки данных, которые мы выполняем в триггере и которые нарушают ограничения других таблиц, не только терпят неудачу, ио и как только триггер завершит работу, транзакция откатывается, и никакие дальнейшие команды не выполняются. Имеются три основных пути, чтобы управлять ошибками в наших триггерах, которые могут быть объединены следующим образом: □ проверка, что значения в таблице правильные; □ каскадирование операций; □ гызов хранимой процедуры.
Проверка правильности значений в таблице Основной метод, который мы будем использовать, чтобы проверить правильность условий, следующий: IIF EXISTS ( SELECT * FROM INSERTED WHERE «условие равно True для неправильных значемийм} BEGIN — всегда формирует другую о»йку, чтобы сообвить выеывашт«г( где она находится RAISERROP. 50000 'В размешенной таблице имеются неправильные данные' ROLLBACK TRANSACTION RETURN — заметьте, что триггер не эавершит работу, пока вы не — остановите его, так что пакет завершается, когда триггер — AFTER завершит работу _ END Здесь проверяется маличив в жвсеиве етзяяти? мелентов, которые удовлетворяют условию неправильных значений, и если они ерщеетврют, формируется собственное сообщение об ошибке и производится откат транзакции. Каскадирование операций В следующем фрагменте кода мы покажем, как каскадировать обновления одной величины в другой таблице. DECLARE BerrcrHald. int, — используется для хранения ошибки после оператора gmsg varchar (8000, — обновления для формазирувшЯ' соовшеиий UPDATE к.nothe-гTable SET «столбец» • «значевд®» FROM anatherTable JOIN INSERTED ON another!able,tableld - INSERTED.tableld SET 8errorHold “ 8 terror получение змчния кола сшибки после обновления IF BeirorHold О & — что-то неправильно BEGIN SET (Jinag - 'Ошибка ' t CAST(веггогНоХа) + ' вс-зиижла при каскадировании обновления в таблице’ — всегда формирует другую ошибку, чтобы сообщить вызывающему, — где она возникла RAISERR3R 50000 dsg ROLLBACK TRANSACTION ' RETURN — заметьте, что триггер не завершит работу, пека вн не -- остановите -его, так что пакет завершается, когда триггер — AFTER завершит работу END Здесь для формирования сообщения об ошибке используются две переменные; gerzorHold (хранение кода ошибки) для размещения кода ошибки и gmsy (сообщение ) дм формирования текста сообщения об ошибке. В этом фрагменте в строки таблицы anotherTable (другая таблица), у которых идентификатор строки (tableld) совпадает с одним из идентификаторов таблицы IHSERTBD, в заданный столбец помещается заданное значение. После этой операции в переменную gerrarHald помещается код ошибки, и если он отличен от нуля (ошибка произошла), формируется соответствующее сообщение, инициируется собственная ошибка и организуется откат транзакции,
Выполнение хранимой процедуры Следующий фрагмент кода демонстрирует метод вызова хранимой процедуры из нашего триггера: DECIARE garrorBold int, — используется для хранения ошибки после — оператора оСнсвленич и размеаения — sas-sp*ii»<»Moft хранимой процедурой 0returnValue int — величины для форматируемых сообщений gmag varchar(8000) ВХЕС SreturnValue » <имя_процелусы><лараиетры> SET eerrorHold = «error — получение кода ошибки после обновления IF eerrorHold <> 0 or вreturnvalue <0 — что-то произоило BEGIN SET (msg - CASE WHEN ferrorSold <> 0 THEN 'Ошибка ' + eAST(terrorBoW) + ' произошла при каскадированном обновлении еабищы'’ EbSE ' Возвращаемая величина ' + CAST (в return Value Ав varchar (1ЯЦ * + ' получена из процедуры' END -- Всегда формируется другая ошибка, чтобы сообщить вывываювему, — где она произошла RAISERROR 5Q000 #msg ROLLBACK TRANSACTION RETURN — заметьте, что триггер не завершит работу, пока вы не — остановите его, так что пакет завершается, когда триггер — AFTER завершит работу END В этом фрагменте используются три переменные: Йегг-охвоЫ (хранение ошибки) для размещения кода ошибки, greturaValue (возвращаемая величина} для размещения величины, возвращаемой хранимой процедурой, и ffmsg (сообщение) для формирования текста сообщения об ошибке. Здесь запускается хранимая процедура, имеющая заданное имя и заданные параметры, и фиксируется возвращаемая величина в переменной greturnVelue. После завершения работы процедуры в переменную gerxorHold помещается код ошибки. Если во время работы процедуры произошла коллизия (код ошибки не равен нулю или возвращаемая величина меньше нуля), формируется собственное сообщение об ошибке; в первом случае формируется сообщение, что при каскадированном обновлении произошла ошибка, в во втором случае выводится возвращенная процедурой величина. В обоих случаях осуществляется откат транзакции. Как мы видели, сообщения об ошибках, которые мы получаем при нарушении ограничений, ни удобочитаемы, ни достаточно значащие. Однако когда мы строим триггеры, то пишем наши собственные сообщения ей ошибках, которые могут быть сделаны настолько удобочитаемыми я значащими, кек захотим. Мы могли пойти далее и поместить каждый предикат ВД в наши триггеры (включая уникальные ограничения), но это потребовало бы большего объема кодирования и не будет выгодно для нас: триггера! принципиально медленнее, чем ограничения, и не являются лучшим методом защиты наших данных, кроме случаев, когда ограничения ке могут использоваться. 14—1868 399
Обработка ошибок в вызываемом коде Большим достоинством обработки ошибок. в триггерах является то, что так как при возникновении ошибок вся последовательность команд триггер* откатывается наем, не нужно заботиться относительно внешнего управления ошибками, кроме обработки состоянии ошибки при защите от ошибок, связанных с ограничениями. Триггеры AfTSR Как мы раньше сказали, триггеры AFTER названы так, потому что они запускаются после того, как данные фактически окажутся в таблице. Это позволяет ням проверить, независимо т того, что оператор сделал с данными в таблице, не нарушают ли они наши спецификации. В предыдущих версиях SQL Server это был единственный вид доступного триггера, но вместо ключевого слова AFTER вы использовали, ключевое слово ГОЖ В SQL Server 2000 последнее ключевое слово больше не должно использоваться как тип триггера, но оно сохранено для обратной совместимости. Давайте разберем простой пример триггера AFTER, рассматривая состояние таблицы INSERTED и содержание таблиц ВД во время выполнения триггера: I CREATE TRIGGER aet-istStrlnsertDenso ON artist AFTER INSERT AS SELECT ' ссдержисс е тавлииы INSERTED' SELECT artistld, паи» — только перьичнкй клич и имя, — чтобы, разместить не странам» книги FROM INSERTED SELECT 'содержимое табдашы artist' SELECT artistld, name FROM artist SO Затем мы выполним оператор INSERT для вадаиня имени: - I INSERT INTO artist (палий - VALUES ('jethro toil’) который возвратит следующий набор результатов: содержимое таблицы INSERTED artistld name 27 jettiro tufl содержимое таблицы artist
artistld name 27 jettiro tull 1 the beetles 2 the who Таким образом, мы видим, что строка в таблице INSERTED содержит данные вставленной строки (что естественно) и мы видим из результатов второго запроса, что таблица уже содержит значение, которое мы поместили в нее. (Если бы мы делали это е триггером INSTEAD OF, второй запрос ие возвратил Вы нам новую запись.) Давайте теперь расширим наш пример с несколькими трштерами, рассмотренный выше, изменяя кодирование триггера tableASaf terUpdateSdemoNestRectirse, чтобы помести» нялваяие процедуры в поле tableB. field, что позволяет нам вида» различные запускаемые триггеры: ALTEP. TRZ-SGER гасшАГа t ГегТршшЗагтоЯеяскесигзе ON tableA AFTER INSERT AS SELECT 'триггер довавлания в tableA' INSERT INTO tables(field) VALtJES (object_nan» (f BprocldJ) ttprcclfl дает cbject_id (идентификатор) — текуяей выполняемой пронелуры GO " ' Затем создадим еще два триггера, идентичные первому (но с другими именами)? CREATE TRIGGER tebleASafterUpaateSdempNestReauraeS ON tableA AFTER INSERT AS SELECT 'триггер 2 добавления в таблицу А' INSERT INTO tableB(field) VALUES (object_naflie (Mprocld)} GO CREATE TRIGGER tableASaftertJpdateSdeeoNestRecutsel ON tableA AFTER INSERT SELECT 'триггер 3 добавления s таблицу A’ INSERT INTO -tableB(field) VALUES (c-bject_nane (Mprocld) J
... и поместим одну строку I INSERT INTO tableA VALUES ('Проверка значений') ... что возвращает три следующие строки: триггер добавления в таблицу А триггер 2 добавления в таблицу А триггер 3 добавления в таблицу А Чтобы показать, что таблицы таковы, как мы ожидаем, выполним для таблиц следующие операторы: SELECT * FROM tableA SELECT • FROM tables ... и мы увидим, что tableA содержит единственную строку, как мы к ожидали, atableB содержит строки, являющиеся результатом каждого из трех триггеров таблицы table А, которые были выполнены: field Проверка значений field tableASafterUpdateSdemoNestRecurse tabieA$afterUpdate$demoNestRecurse2 tablcA$afterUpdatc$dcmoNcstRccurse3 Наконец, мы покажем, как переупорядочить последовательность выполнения триггеров: EXEC sp_settriggerord*r etriggerKame - ’tebleASafterUpdate5demoMestRecurse3', eorder - 'first', И Sstnittype = 'insert' Здесь gtriggerWame — имя триггера; Border — порядок; Вstmt type — тип оператора, запускающего триггер. Заметим, что Border может иметь значение ' first' (первый), ' last'(последний), или ' попе' (никакой), чтобы запустить триггер в неустановленном порядке. Мы можем устанавливать запуск только первого и последнего триггера, в то время как любые другие триггеры запускаются в установленном (как мы задали) порядке, Значение Istwttype может быть 'insert' (добавить), 'update' (обновить) или 'delete' (удалить).
Мы можем теперь выполнять команды, чтобы удалять элементы т-аб.лкцы, вставлять строку и, наконец, выбирать строки из таблицы: I TRUNCATE TABLE ИЫеА TRUNCATE TABLE tableB INSERT INTO tableA VALUES ('Проверка значений'i SELECT * FROM table* SELECT * FROM tableB ... и в этом случае возвратим другой порядок триггеров: field Проверка значений field tableA$afterUpdate$demoNestRecurse3 tabieA$afterUpdate$demoNestRecurse tabieA$afterUpdate$demoNestRecurse2 В качестве заключительного примечания в этом разделе я вы предостерег от использования нескольких триггеров для одной и той же операции в одной таблице. Часто проще создать единственный триггер, который управляет всеми мгаимв потребностями. Обычно мы проектируем наши системы, используя один триггер INSTEAD OF и один триггер AFTER для каждого действия, если его необходимо. Мы будем также редко использовать многоцелевой триггер, который будет запускаться и оператором INSERT, в оператором UPDATE. В основном наш принцип должен заключаться в том, чтобы иметь триггер максимально простым для избежания любых проблем с упорядочиванием. Использование триггеров AFTER для решения общих проблем В атом разделе мы расежотрим, как вы используете триггеры, чтобы решить следующие типичные проблемы: □ каскадирование добавлений; □ проверки давпазонов; □ каскадированные удаления установкой значений таблиц-потомков в NULL. Важно знать, что независимо от того, какие операции мы выполняем с помощью триггеров, все зависит от существующих ранее данных в вашей таблице, для которых вынолнены все требования ограничений. Например, не стоит вставлять строки таблицу-потомок (и, следовательно, вызывать запуск всей последовательности триггеров/ограничений), пока данные предка ив проверены. Одновременно не следует проверять состояние всех строк в нашей таблице, пока мы яе завершили все наши изменения с ними; то же самое можно сказать об операциях каскадирования удалений. Три последующих примера — всего лишь маленький набор возможных использований триггеров; они — только образец того, как все происходит. Каждый из фрагментов, которые мы покажем в следующих трех подразделах, будет подходить для триггеров (любого типа) и будет иметь следующий основной формат:
CREATE TRIGGER ОЯ <имя_тавлицк> FOR «операция» AS — Цель: Триггер для «операции», который запускается для давай «операции» языка манипулирования данньаи (®4Ц) BEGIK DECLARE growsAffected int, — размещение числа измененных строк gerrerNumber int, =- используется для размещения кеда сиибки — после выполнения оператор» ЯМД 8msg varchar [255), — используется дня размещения — сообщения ав оажВке gerrorSewerlty varchar (20), — степень owOtat gtriggerNaraft gysname — имя триггера SET в-гоизМfected - ggrowcaunt SET gtriggerHame - objeet_name(ggprocldl — используется для освещений — не нужно продолжат», если нет измененных строк IF JrowsAffected « 0 RETURN «здесь помещаются фрагменты» END Мы будем обычно писать наши триггеры так, что, когда произойдет ошибка, тосформируем ошибку и выполним откат транзакции, чтобы остановить любые дальнейшие команды. При каскадировании добавлений мы имеем ситуацию, когда пойле того, как запись добавлена а таблицу, одна или несколько других новых записей автоматически добавляется другие таблицы. Это особенно важно, когда мы создаем принудительно отношения "един к одному" или "один ко многим”. Например, пусть мы имеем следующий наоор таблиц: urlSta’JS wtSWwtfd: in* НОТ NULL ut1________________________ uriid: int NOT NULL tirtt varghariW) HOT HULL игвЯЫЗДеИ: int NOT NI*L(FK} urtttMNOTWLMRq ----f----- I i--------- i иВайвТуре j____________ WstatusType: int NOT NULL rams: varchar(60) NOT NULL defaultH- bit NOT NULL - Здесь urlStatus — таблица "состояние URL"; arlSCatasId — идентификатор состояния URL; urlStatusTypeld — идентификатор типа состояния URL; URL—таблица "адрес в Интернете"; arlld — идентификатор URL; urlStotueType таблица "тип состояния URL"; пято — название (типа состояния URL); detaultFl — флаг задания по умаляаяию.
В зтом случае таблица uxl определяет множество URL для нашей системы, в то время как отношение между url и urlStatus — "одни ко многим". Мы начинаем построение триггера» который вставляет строку в таблицу urlStatus с помощью оператора INSERT, который «задает яовую запись бвгПй в arlStetusType, используемым » умолчанию на основе defaults’!, имеющего значение 1. (Мы пока предположим, что имеется максимум одна строка с defaults'!, равным 1, и выполняется проверка, что это имеино так.) — довавяение записи к таблице urlStatus, сообщая ей, что эта новая запись — должна вначале рассматриваться как состояние по умолчание INSERT INTO uriStacus(urlla, urlStatusTypeld) SELECT INSERTED.urlld, urlStatuSType.urlSrarueTypeld FROM INSERTED CROSS JOIN uriStatuaType -- использование CROSS JOIN с оператором WHERE, — как в случае, фактически на является — объединением. INSERTED и urlStatusType WHERE uriStatusType.defaultEL = 1 SET веггохЫшвЬег « вЙеггог и IF BerrorHumber о О BEGIN SET 8»eg - 'даиака: ’ + CASTiierrorNurber AS varchaedOM + ‘ возникла во время сведения строки таблмш uzlStatus*’ RAISERROR 50000 gmsg ROLLBACK TBANSACTIQN RETURN END Триггер мог вы также быть необходим для запрещения удаления urlStatusType, используемого по умолчанию, так как мы будем всегда хотеть .иметь значение urlStatusType, используемое по умолчанию. Однако формирование такого триггера создаст ситуацию, где мы хотим: □ удалить строки таблицы uri, но не можем из-за существующих строк таблицы urlStatus; □ удалить строки таблицы urlStatus, но не можем, так как они — последние для строк url. Это — трудная ситуация для решения с использованием обычных триггеров и ограничений, и может быть легче решена, добавляя поле к таблице, сообщающее триггерам (или ограничениям) что запись доступн* для удаления. Это — общая проблема в случае таблиц, где вы хотите осуществить дауетороивее отношение, но при некоторых обстоятельствах хотите реализовать одностороннее отношение. Проверка диапазона Проверка диапазона просто означает, что определенный диапазон данных сооотетствуееу правилам проверки данных. Некоторые примеры: О Составление баланса счета, чтобы удостовериться, что нет отрицательного баланса я результате сделки, □ Обеспечение того, что для отношения существует соответствующее число записей (мощность), типа отношения "один к между пятью и семью". □ Проверка, что в таблице (или в группе строк в таблице) существует только единственная запись, которая является первичной или используемой по умолчанию.
В нашем примере в убедимся, что в таблице имеется нс больше одного установленного по уиолчапио флага, равного True или 1; другие должны быть О или False, Мы таюке поместим код (что является хорошим стилей), который будет брать любое новое значение, где поле def aultFl рявно 1, и устанавливает же другие в 0. Если пшндаатель вручную уетанонит больше одного аяяита defaultFl в 1, то проверка, сделанная впоследствии, должна отменить эту операцию. Мы будем использовать в качестве примера таблицу urlStatusType, которую создали ранее: urtSatesTypa_________________ u г IStatu sType: Int NOT NULL name: varcharfeo; NOT NULL defaultFl: ort NOT NULL и выполним проверку диапазона следующим образом: — если столбец defaultFl Сия модифицирован IF UPDATE(defaultFl) BEGIN — обновление левой другой строки «аблицм uzlStatusType, задавая — значения, не равны* значение по умолчанию, eaiat введенная строка — установлена по умолчанию UPDATE urlStatuaType SET defaultFl - О FROM urlStatusType — только строки, уже установленные по умолчанию WHERE urlStatusType.сеfaultFl - 1 — и не вставленная строка AND urlStatuaTypeld NOT IN (SELECT urlStatttslypeId FROM INSERTED WHERE defaultFl - 1 ) SET SerrorNuabex - SSerrcr IF 9errorNunber <> 0 BEGIN SET ®msg - 'ОшиЪкал ' + CAST(e«rrorMu«ber AS varchar(10)) f ' возникла при овиовлемж* значений defaultFl’ RAISKRROR 5MM gmsg ROLLBACK TRANSACTION RETURN END — смотрим, нет ли боле» одной строки, установленной ио умолчании, если — пользователь установил их в одной операции IF ( SELECT COUNT (*} FROM urlStatusType WHERE urlStatualype.defaultFl - 1) > 1 BEfilN SET ®meg - 'Слишком много строк с флагом по умолчани» - 1' RAISERROR 50000 imag ROLLBACK TRANSACTION RETURN BHD END
Айтгддармвмм удалений установкой значений таблицы-потомка в NULL Когда мы имеем таблицы е необязательными вторичными ключами, вместо каскадирования, когда удаляется предок, мы можем иногда пожелать удалить связь и установить все ссылки внешнего ключа в MULL. Чтобы сделать это, в триггере, связанном с оператором DELETE, мы просто обновляем каждую строку в таблице-потомке, которая ссылается на значение в строке (строках), которую мы удаляем. Возьмем, например, следующее отношение: Здесь в дополнение к предыдущим обозначениям сатрапу — таблица "компания"; companyld — идентификатор компании; папе — название (компании). Таблица url содержит все адреса Интернета для нашей системы, и таблица company использует одно из значений для каждой компании. Веля мы шш удалить одну или несколько «писей таблицы url, нам не нужно удалять все компаний, которые используют эти значения URL. Это мы могли вы реализовать, задавая строкам-потомкам ввачения NULL вместо удаления самих строк. Так как SQL Server осуществляет каскадирование только удалением, когда удаляется строка-потомок, мы должны использовать триггер. Код триггера, который делает это: UPDATE с стр а г. у SET urlld = NULL FROM DELETED JOIN соирапу ОЯ DELETED.urlld = company.urlld SET eerrorNumber - ggerror IF 0*rrerMumber <> 0 BEGIN SET Bmsg = гОиивка: ' + CAST(8«ror»umber AS varch»r(10>} + 'возникла во время каскадного удаления установкой » WLL' FAISERROR 5000(1 gmsq ROLLBACK TRANSACTION RETURN END Триггеры fNSTEAD OF Триггеры INSTEAD OF запускаются вместо операций манипулирования с данными ж до того, как выполняются ограничения-проверки. Вы можете иметь только единственный триггер INSTEAD OF для каждой операции в каждой таблице. Обратите внимание, что хотя этот триггер обычно упоминается как "триггер до...”, он означает нечто большее. При кодаровании триггера INSTEAD OF мы должны переделать операцию в триггере. Например, в случае операции INSTEAD OF DELETE триггер содержит удаление точно так же как операция, которая первоначально запускает триггер.
Следующий пример показывает триггер, который возвращает состояние таблицы inserted f затем физической таблицы. I CREATE TRIGGER artietSinsteadOfInsert OK artist INSTEAD OF INSERT AS — вывод содержимого тевлит* INSERTED SELECT ' содержимое тевлицы INSERTED' SELECT * FROM INSERTED -- вывод содержимого физической оаВяииы SELECT ' содйрюмое таблицы artist' SELECT * FROM artist GO Далее, если мы выполним добавление в тавлнцу: I INSERT INTO artist(name) VALUES ('John Tests') ... то получим следующий результат содержимое таблицы INSERTED artistld name 0 John Tesh содержимое таблицы artist artistld name 1 THE BEATLES ... 2 THE WHO Это — точно то, что мы ожидали; триггер запускается перед тем, как столбцы будут помещены в таблицу, что объясняет, почему запись John Teeh (Джон Теш) не появилась во втором результате. Однако, если мы посмотрим содержимое таблицы после выполнения- триггера: SELECT artistld, name | FROM artist ... мы получим: _ - artistld name 1 THE BEATLES ... 2 THE WHO
Данный результат иллюстрирует одну трудность с триггерами INSTEAD OF, а имение, что любые вставки добавляются к таблице INSERTED или DELETED, а не к БД, Это означает, что мы должны вручную вставить данные в таблицу, чтобы выполнить работу триггера должным образом. Поэтому триггеры INSTEAD OF вынуждают вас полностью повторно изобрести путь, которым мы закодируем операции по манипуляции данными, чтобы максимально использовать их. В оставшейся части этого раздела иы рассмотрим некоторые другие проблемы триггеров instead OF, связанные с парой важных использований, которые делают их очень полезным дополнением к комплекту средств SQL Server. Предостережения Проблемы,7 связанные с триггерами INSTEAD OF, следующие: □ Вы должны повторить операцию, выполняемую пользователем; □ нелегко выполнить контроль границ для нескольких строк; □ триггеры INSTEAD OF игнорируют операции каскадирования. Мы рассмотрим каждую из трудностей несколько глубже в следующих разделах. Вы должны повторить операцию, выполняемую пользователем В следующем триггере мы показываем простую демонстрацию этого: CREATE TRIGGER artiatSinsteadOfInsert ON artist INSTEAD OF INSERT - - = AS — должны иьоиировать операцию, которую мы выполняем в триггере INSTEAD OF. — заметим, что нужно определить список столвцов для размещения данных -- и ьм не можем просто выполнить SELECT * FROM INSERTED ид-sa - - столбца идентификации, который не допускает помещение в него значения. INSERT INTO artist (name, defaultFl, catalagHuraberMaskJ SELECT name, defaultFl, catalogNunsberMask FROM INSERTED GO CREATE- TRIGGER artistSinsteadOfDelete ON artist INSTEAD OF DELETE AS - -- должны имитировать оперение, которую мы выполняем в триггере INSTEAD OF. DELETE FROM artist FROM DELETED — мы всегда присоединяем та&пицы INSERTED и DELETED — к реальной таблице через перзичнь:й ключ. JOIN ARTIST ON DELETED.art1stId - artist.artistld GO
Создав триггеры, чтобы работать вместо операций INSERT и DELETE, мы проверим их: I INSERT INTO artist(пая», default.Fl, catalogNumberMaek) VALUES ('raff!', 0, 'V> SELECT * FROM artist WHERE narr.e - 'raffi' ... что возвратит: artistM name ... 14 raffi ... Затем мы удалим строку и выполним оператор SELECT, чтобы проверить, существует ли она: IDELRTE FROM artist WHERE паве - ’'raffi’ SELECT * FROM artist WHERE паев - 'raffi' ... что ничего не возвратит, так как удаление выполнено правильно. Нелегко выполнить контроль границ для нескольких строк Так как в триггерах AFTER давяые уже были добавлены к таблице, легко осуществить контроль, если будет многострочное ограничение. Однако трипер INSTEAD о Г не размещает данные в ВД. В следующих двух примерах кеда мы рассмотрим два различных метода для выполнения проверки, что таблица имеет неотрицательный баланс дяя паяя amount (сумма) в таблице accountTransaction, где группируются балансы для одних и тех же значений account I d (идентификатор счета). Триггер AFTER для сравнения IIF EXISTS (SELECT accountld FROM accountTransaction JOIS (SELECT BISTINCT accowitld, FROM INSERTED’ AS accounts OK eccountlranaaction.accountld = accounts.accountld GROUP BY accountld HAVING ewe (amount) > 0) BEGIN RAISERROR 50000 'При операции вовникает отрицательно валаис' END Триггер INSTEAD OF IIF EXISTS (SELECT accountld FROM — операция UNION помешает величины :es;kk для — всех счетов, зависших от оверации (SELECT accountTransactionld, accountld, awaunt FROM INSERTED UNION SELECT accountTr'ansactionld, accountld, amount FROM accountTransaction
JOIN (SELECT DISTINCT aceountld FROM INSERTED) AS accounts ON accountTran-saction.accountld - accounts.accountld — удаление всех записей acctcrtTransaction, которые мы обновляем — удаляя строки, которые теперь находятся э таблиц» DELETED WHERE eccountTxaneactienld: NOT IN ( SELECT accountlransactionld FROM DELETED) ) AS accountvalues GROOT BY accountId HAVING sum(amount) > C) BEGIN RAISERRQR 50000 'При операции возникает отрииатальньй баланс' END В триггере AFTER все, что требуется сделать — проверить текущее состояние таблицы, в то время как в триггере INSTEAD OF мы должны добавить значения ив таблицы INSERTED к значениям фактической таблицы. Одна интересная вещь, иа которую следует обратить внимание, что это не простой запрос к таблице INSERTED. Мы всегда должны рассматривать все строки текущей таблице, которые связаны со стрелами в таблице INSERTED, что требует комплексного оператора JOIN. Триггеры /ЛЛ>/£40 OF игнорируют операции каскадирования Одна из наиболее неприятных проблем триггеров INSTEAD OF — то, что ими игнорируются операции каскадирования, так что использование триггеров INSTEAD OF в обеспечении бизнес-правил может оказаться проблематичным. Например, если бы в данной таблице первичный ключ изменил значение так, что он перестал бы соответствовать проверкам триггера INSTEAD OF, такое изменение не будет предотвращено, так как триггер не будет запускаться:. Эти ограничения означают, что триггеры INSTEAD OF не должны использоваться ни дан проверки бивнес-оравил, которые включают первичный ключ, ни для проверки бизнес-правил, которые включают удаления. Но это не запрещает использовать их для других целей, к которым мы теперь перейдем. Использования триггеров INSTEAD OF Триггеры INSTEAD OF могут использоваться, чтобы автоматически задавать или изменять значения в наших операторах. Зная, что триггеры INSTEAD OF запускаются и до проверки ограничений, и до триггеров AFTER, они могут использоваться для пекущих изменений данных в таблице. Мы рассмотрим три примера того, как они могут использоваться: Автоматическое поддержание полей В качестве примера мы собираемся построить два триггера: INSTEAD OF с оператором INSERT и INSTEAD OF с оператором UPDATE, которые будут автоматически устанавливать первую прописную букву в именах наших артистов вместо формата, содержащего только строчные буквы: CREATE “RIGGER artistSineteedOfInsert ON artist В INSTEAD OF INSERT AS В INSERT INTO artist (name, default?!, catalogNuniberMask) В SELECT dba.stEing$propetCase(naiRe), default?!, catalogNumbexMask В FROM INSERTED’ В GO Продолжение кода на следующей странице
CREATE TRIGGER artlBtSinateadOfUpdate ON artist INSTEAD OF UPDATE AS ОЮАТЕ artist SET name « stringSproperCasetINSERTED, name), defaultFl = INSERTED.defaultFl H catalogNumberMasle - INSERTED.catalogNumberMask В FROM artist -JOIN INSERTED ON artist.artistld - INSERTED.artistld В GO Чтобы проверить на» триггер с оператором INSERT, выполним следующий оператор: — помечаем очевидно неправильно с^орматгфованиое им» INSERT INTO artist (name, defaultFl, catalogNuffiberMask) VALUES (’eLvIs CoStElW, 0, '77') .— затем извлекаем последнюю помещенную величину в эту таВлицу SELECT artistld, паше FROM artist WHERE artistld. - ident_cwrrent ('artist’ Г ' - ... который возвращает требуемый результат: artisfld name 19 -EMs Costeto Далее мы проверим триггер с оператором UPDATE. Сначала проверим, являются ли все величины в таблице правильными: SELECT artistld, name FROM artist Запрос возвратит список неаккуратно сформатированных строк: artistld name 19 EMs Costsflo 1 . THE BEATLES 15 The Monkees 2 THE WHO Далее мы запустим следующий запрос, который формирует набор имен, написанных прописными буквами: IDPDATE artist SET пале « U₽₽ER(name) Однако, мы видим, что все строки не содержат прописные буквы, хотя и сформатировм аккуратным образом:
artistld name 19 Bvis Costello 1 The Beatles 15 The Monkees 2 The Who Триггеры INSTEAD OF — лучший способ выполнить такие манипуляции с данными, так как они спасают нас от размещения неудачных данных, что требует выполнить дои<мшительныи шаг с помощью триггера AFTER для их обновления. Всякий раз, когда вы должны расширить универсальные действия, которые выполняют операции INSERT. 7?DATE или DELETE, использование триггеров INSTEAD OF является лучшим способом. Условное добавление Во время вашей дамонстрации обработки ошибки была создана групповая операция добавления записей, которая добавляла в таблицу альбомы на основе значения catalogNumber (номер каталога), если только маска, которую мы создали, соответствует артисту. Сейчас мы построим более простой пример, где таблица (или таблицы) в такой системе будет допускать любые данные, вводимые пользователем, во при этом размещая любые неправильные даниые в таблице обработки ошибок, так что можно будет позже рассмотреть все проблемы. Сначала мы должны удалить первоначальное ограничение: I ALTER TABLE album DROP CONSTRAINT chfcAlbuntScatalogWwberSfunctionSartistScataiogBwberVelidate В этот момент мы не защищены от любых неправильных значений, помещаемых в пода catalogNumber. Но мы создадим триггер INSTEAD OF, который будет врать все правильные строки и вставлять их в таблицу album (или обновлять их). Если же ови неправильные, мы поместим их в новую созданную нами таблицу, которую назовем aIbumExeeption (ошибочный альбом) и которая будет иметь ту же самую структуру, как и таблица a Ibume другим значением первичного ключа и новым полем, называемым operation (операция) для фиксации добавления ели обновления. CREATE TABLE albwaException I albueExceptionld int SOT KULL IDENTITY, name varchar(60) NOT NULL, artistld int «« NULL, catalogNumber char(121 HOT NOLL, exceptionAetion char(11, exceptionBate datetime Здесь albuatException — ошибочный альбом; albwnExeeptionM — идентификатор ошибочного альбома; name — название (альбома); artistld — идентификатор артиста; catalopNimber — номер каталога; exceptionAction — операция е ошибочным альбомам; exceptionDate — дата ошибки. Теперь создадим простой триггер INSTEAD OF, чтобы перехватить данные пользователя и попытаться проконтролировать номер каталога. Если данные правильные, мы обновляем таблицу, в противном случае они будут добавлены в таблицу ошибок.
CREATE TRIGGER albumSinateadOfUpdate OH album INSTEAD OF UPDATE AS DECLARE BerrorValue int — это переменная для размещения состояния ошибки UPDATE album SET name - INSERTED,name, artistld - INSERTED,artistld, eatalogHwab*!- - INSERTED.catalogNur.ber FROM INSERTED JOIN album OH INSERTED, a Ibwild - album, albumld — обновляем только строки, где встречен критерий WHERE - dbo. albumScaоalogNonxsrValidate i INSERTED. catalogNorricer, INSERTED.artistld) - 1 — выполнение а этой операторе некоторых проверок, что нет сшибки SET SerrorValue - Mexror IF terroxValue о О BEGIN RAISERROR 50090 'Ошибка в добавлении правильной записи альбома' ROLLBACK TRANSACTION RETURN -- получение всех сорок. где критерий не встретился INSERT INTO albumException(пале, artistld, catalogNumber, :ep- exceptionDate) SELECT name, artistld, catalogNumbex, '0*, getdateO FROM INSERTED WHERE NOT ( -- оС-ычно самый простой способ око:: проа ат ь гфитерии -- и зыпслии-ь NOT(WHERE ...» dbo. а 1 bum $ с* taIcgNombe real (date INSERTED.artistld) •= 1 ) (INSERTED,cataiogKomber, SET eerrorValue « Merror IF ®erxorValue <> 0 BEGIN RAISERROR 50000 'Ошибка в регистрации неправильных записей альбомов' ROLLBACK TRANSACTION RETURN END Теперь обновление соответствующей величины в строке можно просто выполнить с т следующих операторов: — обновление таблицы album с яано хорошим совпадением номера каталога UPDATE album SET catalogNumber - '222-22222-22' WHERE name - 'the white album' — затем перечисляем artistld и catalogNumber альбомов в "реальней” еавлице SELECT artistld, catalogNumber FROM album
WHERE n«n* - 'the white album' — a также в таблице ошибок SELECT artistld, catalo^Nuieber, excepticriAot.ion, exceptionDate FROM albumExcepticn WHERE name • 'the white album' Номер каталога соответствует тому, что мы должны получить, и поэтому иет ошибок в таблице albumException: artistld cataiogNumber 1 222-22222-22 artistid cataiogNumber exceptionAction exceptionDate Затем мы сделаем очевидное неправильное обновление: I UPDATE album SET catalagNuiBber = '1' WHERE name - 'the white album' — Эй-ем перечисляем artistld и cataiogNumber альбомов в "реальной* таблиае SELECT artistld, cataiogNumber from album WHERE name = 'the white album' — а также а таблице сшибок SELECT artiatld, cataiogNumber, exceptionAction, exceptionDate FROM albumException WHERE name - 'the white album' Мы видим, что нет скорректированных строк и не возвращены никакие сообщения об ошибках. однако теперь добавили строку к нашей -таблице ошибок, обращая внимание на ошибку: artisttd cataiogNumber 1 222-22222-22 artistld cataiogNumber exceptionAction exceptionDate 11 U 2001-01-07 01:02:59.363 Оставим читателю создание триггера INSTEAD OF e операцией INSERT в дополнение к триггеру е операцией UPDATE. Мы также, вероятно, хотели бы расширить таблицу ошибок, включив некоторый вид причины ошибки, если может быть более одной возможности. Представления (Жычю выполнение добм.тений, обновлений и удалений в представлениях потому что должны выполняться следующие условия выполнении операторов корректировки иредетавленият 3 Операторы UPDATE и INSERT могут лишь тогда корректировать представление, если оии ссылаются иа столбцы только одной таблицы одновременно.
□ Операторы ОВЬЕТЕ могут иеполъаоваться лишь тогда, когда представление ссылается только на одну таблицу. Однако используя триггеры INSTEAD OF, мы можем реализовать механизм операторов INSERT, UPDATE и DELETE для представлений. В этом примере мы создадим чрезвычайно простое представление и поместим триггер для организации добавлений: - — создание представления вез столбца defaultFl, так как ив следуем =- пользователю видеть его в представлении, кроме того хотим — представить имена Оольижи буквами CREATE VIEW vArtistExcludeDefault AS SELECT artistld, UPPER (name) AS name, catalogNuntheiMask. FROM aztLsc GO — далее мы создаем очень проедай триггер INSTEAD OF для добавления CREATE TRIGGER vAxtistExcluddDefaultSinsteadOfInsert ON vArtistExciudeDefault INSTEAD OF INSERT AS BEGIN — замочим, что tat не испольэуем none artistld из таблици INSERTED INSERT INTO artist(name, catalogMumberMask, defaultFl) SELECT паяе, cataloglfumberMask, 0 — используя представление меж — установить defaultFl только в О FROM INSERTED END GO Затем мы просто испольвуем оператор INSERT так же как и для таблицы (исключая столбец идентификаторов, для которого мы не можем задать значение): I INSERT INTO vAztiatExdtideDafault (name, eatalogNwaberMMk) VALUES ('The Monkees', '44') Однако представление ведет себя иначе: Server: Msg 233, Level 16, State 2, Line 1 The column 'artistld' in table 'vArtistExciudeDefault' cannot be NULL. (Сервер: сообщение 233, уровень 19, состояние i, страна 1 Столбец artistld таблицы vArtistExcludeDefauU не может иметь значение NULL) Это не то, wo мы ожидали или хотели иметь. Поэтому мы должны переформулировать наш оператор -INSERT, ’чтобы включить идентификаторartistld и задать ему неправильное значение. Теперь добавленный запрос работает правильно: I INSERT INTO vArtistExdiideDefault (artistld, name, aatalogMebarMask) VALUES (-1, ‘The Stonkeea', ‘44 'j SELECT * FROM vAttistExcludeDefaelt WHERE artistld » ident_cuzrent{'vArtistExciudeDefault’)
... и возвращает значение, которое мы предполагали иметь: artistld name defauitA catatogHumberilasIc 15 . - THE MONKEES 0 44 Следует отметить, что если у яле в представлении исполмутотея две или больше таблиц, и мы выполняем операцию INSERT для дредстявлевя, то могли бы поместить данные во все таблицы, которые составляю» представление. При этом нужно аккуратна обойти требование, что операторы INSERT и UPDATE, используемые для представлений, иогут иметь дело только, с единственной таблицей одновременно, которое.является очень тяжелым. (Дальнейшие детали относительно представлений можно найти в следующей главе.) Код клиента и хранимые процедуры В последаее время появилось программное средство перемещения большинства ='= бизнес-пряяил и коде защиты данных иа SQL Ser ver в набор интерфейсных объектов промежуточного уровня. Таким образом, ВД, клиент и би внес-прав ила размещаются в трех независимых реализующих их единицах. В этом случае биэнес-правила, е которых мы, возможно, думали, что их хороню реализовать через ограничения и триггеры, перемещаются из этого слоя ’'данных” в код, использующий клинетскую часть типа СОМ-объекта или хранимых процедур. Такой многоуровневый проект также предназначен облегчить жизнь пользователю, так как пользователи редактируют данные, используя обычные клиентские средства, в то время как сервисы промежуточного уровня обеспечивают защиту данных, которые проходят черев них, таким образом изолируя пользователей от всего, что требует SQL-кода. Но не только яте, сервисы могут также непосредственно .унрвдши» левыми атиЯшмаги, которые формирует и выдает пользователь, создавая осмысленные сообщения ой ошибках. Так как прикладные пользователи прежде всего редактируют строки по одной, нежели сразу большое количество строк, это на практике работает достаточно хорошо, . . . . Другой момент заключается в том, что в большинстве приложений для предприятий (например, ситуации, с сотнями тысяч пользователей Web-сайта) БД обычно рассматриваются как "узкое место системы". Хотя и возможно разместить нагрузку на единственном сервере, во многих случаях может быть намного легче распределить нагрузку на несколько серверов приложения, чего мы коснулись в главе 9, Однако почта любой механизм защиты данных, который сформирован без использования ограничений или триггеров, может оказаться проблематичным. Давайте рассмотрим наш список возможных пользователей, которых мы ввели в самом начале главы, а именно: □ Пользователи, использующие традиционные интерфейсные средства. Когда все пользователи применяют трядициониые клиентские средства, разработанные для интерфейса, не возникает никаких проблем с использованием промежуточного уровня. Фактически это может дата большие преимущества, поскольку, как мы обсудили, методы объекта, используемые для реализации бизнее-правил, могут привести к уменьшению производительности.
□ Пользователи, непользующие настраиваемые средства работы с давными пт Microsoft Access. Рассмотрим случай, когда пользователь должен изменять набор ’живых" данных, но нуждается в атом только в течение недели или уикэнда, и нет времени написать полноценное приложение. Мы яе смажем допустить непосредственный доступ к ладным- поскольку они находятся 1В сыром незащищенном состоянии. Следовательно, мы Вудам должны или закодировать соответствующее бизнес-правило в ВД Access, или отказать пользователю и заставить его ждать, ваш не будет разработано приложение. Эта ситуация относительно редка, и мы обычно строго запрещаем такай доступ. □ Программы, которые импортируют данные из внешних источников. Почти каждая система любого объема будет включать некоторые средства импортирования данных в сыром формате иа внешиих систем, возможно, от другой части выпей компании или вообще иа другой компании и размещать ин данные в таблице на сервере БД. Это может быть в столь же простой форме, как и приложение пользователя для импортирования электронной таблицы, или в столь же сложной форме, кек интерфейс между всеми школами в ттате и соответствующим департаментом образования штата. Средства имеют диапазон от приложений пользователя до DTS (сервисы ^«образования данных), или даже ВСР (Bulk Сору Profram — программа массового копирования), которая поступает совместно с SQL Server. Когда средний уровень включает все бизнес-правила и проверки целостности данных, мы должны будем или проходить через средний уровень (часто по одаому)» или извлекать все бизнес-правила и кодировать их в наших программах импортирования. □ Необработанные запросы, выполняемые администраторами данных, чтобы выявить проблемы, снизанные с ошибками пользователя. Почти любой с опытом адалинистрирования был вынужден удалять несколько строк из ВД, которые пользователи ошибочно создали, но не мог удалить и по ошибке удалил не те записи, скажем, активные записи счета вместо неактивных. В этой ситуации, если мы имеем бизнес-правила, включенные в триггер, который позволяет удаление только неактивных счетов, пользователю будет возвращено сообщение об ошибке, предупреждающее, что активные счета не могут быть удалены. Очевидно, что мы не можем защититься от сугубо неправильного действия, типа систематического удаления любой строки в таблице, но когда реализована полностью разработанная ВД и защищены данные, используя ограничения и триггеры, почти невозможно сделать даже маленькие ошибки в целостности данных. Как архитектор данных, я предпочитаю возможности, предлагаемые многоуровневыми разработками. Однако бремя бизнес-правил и реализация правил целостности данных должны быть "распредалены” между промежуточным уровнем; и сервером БД соответствующим образом. Два очень конкретных типа таких правил, которые лучше всего реализовать в БД, следующие: □ Любое правило, которое может иметь значение NULL, быть внешним ключом или ограничением-проверкой — Это происходит вследствие того, что прй создании дополнительных внешних интерфейсов такой вид проверок будет обычно требовать довольно немного усилий по кодированию. Кроме того, целостность ВД нужно гарантировать на самом низком из возможных уровней, который позволит закодировать столько программ, сколько возможно для БД. Как заключительный аргумент, эти ограничения будут использоваться оптимизатором, делающим работу запросов быстрее.
□ Правила, которые требуют межтвбличных проверок — Это случай, когда при сохранении значения мы должны проверить, существует ли значение в других таблицах. К дополнительной таблице следует обращаться автоматически, чтобы проверить, что ожидаемое состояние все еще существует. В некоторых случаях промежуточный уровень будет стараться кэшировать данные ВД, чтобы исползовать их при проверках, но нет никакого способа распространить эти кэшированные дивные яа несколько серверов таким образом, что значение, которое ми вводим, имеет надлежащую целосткоегь данных. Рассмотрев некоторые ад возможных недостатков кодирования на промежуточном уровне, разберем теперь случаи, когда хранимая процедура или код пользователя — оптимальнее место для размещения бизнес-правил. Изменчивые правила Эти правила таковы, что для данного набора критериев в одном случае, когда они встречаются, правило принимает значение True, и действие выполняется, в то время как в другом случае правило принимвет значение False, и выполняется другое действие. Лучшее лакмусовое испытание для такого правила — посмотреть, может ли пользователь обойти его. Например, в нашем примере ие этой главы мы могли бы попытаться реализовать правило: "Пользователь должен ввести соответствующего артиста для альбома''. Напомним, что мы имеем таблицу album, которая имеет отношение внешнего ключа (который не может принимать значение HULL) к таблице artist. Следовательно, мы должны поместить подходящее значение artistld, когда изменяем таблицу. Отсюда мы имеем два направления действий. □ Сделать поле artistld в таблице albuia таким, что оно может принимать значение NULL, и, следовательно, необязательным, позволяя пользователю выбрать альбом без задания значения artistld. Чтобы следовать правилу "должен", интерфейс вынужден тогда оргяннзлвять что-то вроде диалога* спросив пользователя: "Вы уверены, что не хотите назначить артиста дай этого альбома?" □ В качестве другого варианта мы могли бы перефразировать бизнес-правило следующим образом: "пользователь мог бы ввести и неправильного артиста". 'Эт означало бы, что ВД могла бы позволить любое значение artistld и действительно позволяет приложению клиента проверять значение, чтобы увидеть, правильно ли оно, и затем организовать диалог: "Вы уверены, что не хотите назначить артиста для этого альбома? " или, что еще хуже: " Вы ввели неправильное значение artist Id и должны ввести правильное". Мы были бы вынуждены пропустить проверку соответствия » ВД в случае, если пользователь говорит: ”Ха, пусть будет введено неправильное значение." Особениость, которую я стараюсь здесь отметить, заключается в том, что SQL Server не может организовывать диалог с пользователем. Жесткий и неизменчивый триггер и проверка ограничения все еще зависят в значительной степени от процесса передачи запроса и ожидания* успешно ли они завершены, и мы нуждаемся в более интерактивном методе связи, где можем влиять яа события после того, как запрос был представлен, и до того, как результат будет возвращен. В следующих примерах мы рассмотрим ситуации, связанные с правилами, которые не могут быть удовлетворительно реализованы с помощью триггеров и ограничений.
Как известно, кто-игцет, тот всегда ноаЛт, Эмо дозложио, используя - временные таблицы. как механизм сообщений, чтобы"передавать” значения триггеру или ыраничвнию. В -этом случае мы можем произвольно корректировать функциональные возможности. Однако триггеры и ограничения обычно не считаются- удобными для реализации меняющихся бизкестравил. Необязательное каскадирование удалений Каскадированные удаления имеют большие возможности, но вы должны использовать их с осторожностью. Как мы обсудили, аии автоматически удаляют записи-потомки, которые зависят от содержания удаленной записи-предка, и нам редко требуется предупреждать пользователя относительна существования этих записей-предков прежде, чем они будут удалены. Однако это не было бы лучшим вариантом в случае банковского счета. Пусть мы имеем следующие таблицы: Здесь account — таблица “счет”; accountProp&rty — таблица "свойства счета"; transaction — таблица “сделка”, В этом случае будет иметь смысл автоматически каскадировать удаления записей таблицы accountproperty, если ми хотим удалить завись из таблицы account. Однако если счет связан с таблицей transaction, мы должны гарантировать, что пользователь знает об этом, и, следовательно, предупреждать его, если требуется тамжидироаяние удалеетя. Это, конечно, увеличит сложность, так как мы не сможем удалить счет, его свойства и сделки в единственном операторе. Вместо единственного оператора мы будем должны выполнить следующие шаги: □ Запустить оператор SELECT для каждой таблицы-потомка, которую юз мстим необязательно каскадированно удалить, чтобы показать пвяьзователю, чв там существует. □ Если пользователь соглашается, выполнить оператор DELETE для каждой таблицы-вотомка, которая имела записи, связанные с нашей первичной таблицей, и в агон же элементарной операции удалить нужную строку. Этот код мог вы выть включен в отдельную хранимую процедуру, которая проверяет потомков, и если они существуют, возвращает табор строк для пользователя, чтобы видеть, какие из них следует удалить. Она будет также включать параметр, позволяющий пользователю игнорировать существование таких строк, продолжить и удалить их. Следующий момент — как мы можем управлять этой ситуацией (обратите внимание, что для ясности примера мы удалили транзакции и обработку ошибок, которые будут подробно рассмотрены в следующей главе).
CREATE PROCEDURE accour>t$delete ( gaccouiitld .int, IreraoveChildTransactionsfl bit -O' ) AS " ' - — если арвеяг уладавь вались, яагуиалим. ее - IF BrenoveChildTraaeactionsFl - 1,- г - DELETE ; — яи .табяшм- аммм* с хлячевш елевой - WHERE accounted. - eaccountld., . . ELSE — проверить существование BEGIN ' IF EXI STI (SELECT * FROrt ttransactionI : WHERE accountld - iaceduntldl ' BEGIN RAISERROR 50000 'Потомок в тавлиие transaction существует* RETURN -100 - - - EMD : - -- : ., - END _ , .... DELETE account WHERE account-id « gaccbuntld' Здесь создается процедура с двумя передаваемыми параметрами: iaccowtTd типа int, в котором передается идентификатор СЧ^тЛркаторый должен выть удален, и gremoveChildTraasactiQnsFl типа bit для передачи флага удаления записи-потомка (если равен 1 или True, запись должна выть удаЛена). В этой проч«^Р« проверяется значение флага SremoveChiIdTransactionsFT, и «ли ои равен !, то запись из таблицы-потомка transaction удаляется. Если же этот флаг равен О, проверяется, существует ли соответствующая запись в таблице-потомке, и «ли существует, пользователю выводится сообщение, что такая запись существует, но само удаление не происходит и. происходит выход из процедуры, В заключение процедура удаляет запись из таблицы-предка, account. - - Теперь пользователь может попробовать выполнить хранимую процедуру с флагом, равным 0, и если какие-либо потомки существуют, псльвователь получит уведомление. Вели же нет, счет будет удален, и, возможно, также и свойства будут удалевы через отношение каскадирования. Правила, основанные на том, как представляет их человек Это иечальиая действительность для системного архитектора, которую нужно учитывать. В типичной системе мы будем иметь -много бизнес-правил, которые нвжутея-жесткими и неизменчивыми и которые нельзя нарушить. Однако большое количество изменчивых правил выглядят жесткими и неизменчивыми. В качестве примере рассмотрим следующее утверждение: У компании политика такова, что мы ве отправляем изделие заказчику, пока не получим оплату. Ойо не кажется проблематичным, пока не возникнет просьба об отгрузке как' жест доброй воли.
В качестве еще одного примера рассмотрим следующее правило: Изделия будут отправлены в той же последовательности, в какой были получены заказы» Рассмотрим случай системы управления качеством, которая имеет деле с изделием Y. Пусть ограничение применяется так. «о данная спецификация заказчика должна быть выполнена, ииаче изделие ие может выть отправлено заказчику. Таким образом, пользователь вызывает и запрашивает большую отгрузку, а клере, который управляет этими заказами, вызывает интерфейс пользователя, чтобы выбрать некоторое изделие Y, чтобы его отправить. Одиако нет достаточного количества изделий, адобы выполнить заказ. Когда клерк связывается с заказчиком, чтобы сообщить ему об этой проблеме, заказчик обычно просит отправить подходящую продукцию вместо ожидания, когда будет большее количества изделия Y, При этом клерк теперь обнаруживает, что в соответствии с бизнес-правилами он не может обработать этот запрос и должен послать его менеджеру заказов для выполнении. Использование хранимой процедуры в этой задаче могло вы позволить пользователям различных групп безопасности отменить данные наавачеяня, когда это нужно. Таким образом, клерк может получить список изделия Y для отправки, выбрать материалы, добавить их к заказу и нажать кнопку для начала отгрузки изделий, вызывая хранимую процедуру produc tor de г 5 ship (порядок отправки издедай). Эта процедура должна проверить, что изделия находятся в пределах спецификаций заказчика, и затем установить порядок отправки. Вели изделие выходит за пределы спецификации, процедура отменит пользователя. Однако менеджер заказов будет иметь возможность выполнить другую хранимую процедуру, например, productOxderfshipQutOCSpec (заказ продукта, отправка за границами спецификации), которая позволила вы пользователю отравить изделия, находящиеся за границами соецификации, Два замечания к такому сценарию: □ Интерфейс должен обеспечивать информацией, яте наделив оказалось за пределами спецификации и не разрешать пользователю выполнять процедуру productCrderSshipOutQfSpec, но знание этого могло вы выть основано на запросе прав каждой хранимой процедуры (используя функцию PERMISSION). □ Процедуры productOrdertahip и productOrderSshipOutOfSpec, вероятно, будут закончены другой хранимой процедурой, которая будет использоваться для обеспечения фактического начала отгрузки заказа. Однако, представляя фактический объект и имена других методов, мы можем ограничить любое ситуационное кодирование в наших процедурах (если этот тип пользователя, делайте это, иначе делайте другое) и представить безопасный интерфейс, основанный только на хранимых процедурах, который используются для Действительностью для архитектора данных является то, что всегда будут те, кто работает "вне правил" как по уместным, так н неуместным причинам, и мы должны принимать это во внимание всякий раз, когда строим систему, дружественную к пользователю.
Учебный пример В нашем учебном примере мы возьмем основную реализацию, которую создали в предыдущей главе, реализуем бизнес-правила, которые были ранее найдены на стадии логического проектирования, и добавим всякие меры защиты данных, которые посчитаемнеобходимыми, bbi будем следовать той же самой дорожкой* которой мы следовали на протяжении этой главы, сначала строя наши ограничения, далее создавая триггеры и затем различные необязательные правила, которые мы выделили. Мы уже назначили типы данных, построили NULL-ограничения и ограничения на внешние ключи, когда создавали наши таблицы. Ограничения по умолчанию Когда мы строим хранимые процедуры, чтобы выполнить давввление в таблицу, то должны перечислить все поля ж подставить значения, даже NULL, в каждое из них. Если при добавлении мы используем значение по умолчанию для пропущенного столбца, это устранит проблемы, которые возникают, когда мы решаем, какие значения используются по умолчанию и какие значения пользователь запланировал, чтобы избежать значения NULL. Обратите внимание, что мы поместили zipCode (почтовый индекс) в таблицу, ссыпающуюся на таблицу city (город). Может показаться, что хорошо позволить пользователю просто выбрать почтовый индекс или город и использовать значение по умолчанию для другого параметра. Однако оказывается невозможно использовать такое значение по умолчанию, поскольку taa. должны ввести значения city й zipCode одновременно, * значения по умолчанию не метут организовать доступ к любым другим полям во время добавления или обновления. Будем использовать эту таблицу как основу для проверок с помощью триггера позже в нашем учебном примере. Поля типа Мв таблице transactionType В таблице transactionType (тип сделки) мы имеем три поля, где можем задать ограничения по умолчанию. Мы установим их для наиболее простых случаев задания параметров. □ requiresSignatureFl (флаг необходимости подписи) — устанавливаем в 0, чтобы указать, что подпись не требуется. □ requiresРауeeFI (флаг необходимости иметь получателя платежа) — устанавливаем в О, чтобы указать, что получатель платежа не требуется. □ allowsPayeeFl (флаг возможности иметь получателя платежа) — устанавливаем 1, чтобы указать, что можно поместить получателя платежа. Все это мы оформим следующим образом: I ALTER TABLE transactionType ADD CONSTRAINT dfltTrar’sactionTypeSrequiresSignatureFLSbi’Sfalse DEFAULT 0 FOR requiresSifnatureFl ALTER TABLE transa"ticnType ADD CONSTRAINT dfltTransactionTypeSrequireaPayeeFlfbitSfalse DEFAULT О FOR. requlresFayeeFl ALTER TABLE transactionType ADO CONSTRAINT dfltTransac-ionType$allowsPayeeFlSbi-$false DEFAULT 1 FOR allowsPayeeFl
Паля номера телефона Другое использование значений по умолчанию — задание полей, которые пользователи обычно оставляют незаполненными. Диа давинлго хороших примера номера телефона — код страны и кед региона. Мы устаяевим-код-страша атаияак-вто — значение па умолчанию для всех телефонных номеров Соединенных Штатов, и, скажем, ’615’ для кода региона ням код региона, где расположены пользователи системы, I ALTER TABLE phoneМ urabeг - ADD CONSTRAINT dfItPheneJJamberScouBtryCodeSstringSiSharSoeberOne DEFAULT ' 1 ’ FOR COUntryCode ALTER TABLE phoneNumber ADD CONSTRAINT ofItPtoneNtsaberSareaCedeSetringSlocaiAreeCoee DEFAULT '615' FOR areacode Ограничения-проверки Почти в любой проектируемой ВД мы будем требовать гораздо больше ограничений-проверок, чем дяю-т триггеры или необязательные правила. Наша ВД не является исключением. В качестве примеров мы добавим четыре различных группы ограничений-проверок, чтобы запретить пустые строки, предохранить ввод финансовых сделок с будущими датами, сформировать спе^|ншацито диапазона финансовых сделок и обеспечить, что адреса могут иметь только некоторые дапагаетные поля в заданных условиях. Мы будем, конечно, строить определяемые пользователем функции, чтобы поддержать многие из этих ограничений так, как нужно. Запрет пустых строк Для многих строковых полей мы будем желать предохранить наших пользователей от ввода, пустых строк. Как мы обсудили ранее в этой главе, можно предотвратить ввод пустых строк используя следующий код: | CHECK (('*' + COALESCE (RTRIM (<етмвец>) , ") + '*') ' »»' ) но на практике мы обычно хотим ввести такие алгоретмы в функв», где это возможно. В этом случае мы получим следующую функцию: CREATE FUKCTIOH stringScheckEntered ( : : lvalue varchar(BOGO) — иаиволыаая ведаадша тип varchar > RETURNS bit AS — используется для проверки, является да введенная величин* varchar пустой — замечание! для Unicode потребуется даполнительнав функция, есда будет нужно BEGIN DECLARE 0recurnVal bit — возвражает только "да® или "нет" — выполнить RTRIM, COALESCE для ’ ' Если BULL, окружить ззездоч нами (*) к выполнить сравнение IF ('*’ + COALESCE (RTRIM(0value>, "> + '«') О SET greturnVal - 1 — не пустая строка. ELSE
Теперь проверим код. Когда возникают проблемы, всегда полезно иметь возможность быстро проверить код, поэтому мы проверим наш код ограничения в случае неправильных и правильных, данных, как показано ниже: Как и ожидалось, первый код возвратит "Пустая строка”, а второй — "Не пустая строка". Далее мы хотим создать наши ограничения-проверки. Нам нужно рассмотреть каждое поле в ВД, которое ммеоздали, а- -также поля и л епт и ф-ик апии, но лучше виияи) нашего списка доменов, В следующей таблиде мм имеем все домены типаvarchar и char, которые раньше идентифицировали, и определяем, которые из них должны быть проконтролированы а помощью ограничений-проверок. Снова имейте в виду, что наши примеры для простоты не рассматривают неамериканские адреса и телефоны = Имя Тип данных Возможность значения NULL Проверка пустых строк addressDeecriptiveLine (строка описания адреса^ а г с лаг (2000) NOT NULL Да addres sStateCode (код штата в адресе) char(2) NOT NULL Да aIphaNumericltanaeг (алфавитно-цифровое число) varchar(20) NOT NOLL Да areacode (код региона) varchar(3) NOT NULL Да countryCode (код страны) Д7.2Г.1) NOT NULL Да
Имя Тип данных Возможность значения NULL Проверка пустых строк description (описание) varchar(1001 HOT Да exchange (иомер коммутатор а) char(5) NOT NULL Да extension (расширение) varchar(20) NOLL Нет, е< случае ЛЖ В ЭТОМ жзвветав* будет i оанача рения, строка ть; для етного подходам firstName (имя) varchar(60) NOT NULL Да lastNatne (фамилия) varchar(60) NOT NULL Да ntiddleName (отчее тво) varchar(60> ~ NOT NULL Нет, если пользователь не это можно отразить пустой строкой паве (имя) varchar(60) NOT NULL Д* phoneNumber (иомер телефона) char(4) NOT NULL Да string (строка) varchar(20) NOT NULL Нм, т случай, В строка понад< танов, вытек сбиться для мощих из
Имя Тип данных Возможность значения NULL Проверка пустых строк username (системное имя пользователя) sysname (определяемый пользователем в SQL Server тип данных, который используется для размещения имен пользователей. Ми будем здесь размещать имен* SQL Server, чтобы помавать реальные имена, так как любому пользователю, кто имеет право входа для контроля, мы будем давать конкретные права просматривать сделки и/или счета на основе схемы действий) NOT NULL Да zipCode (почтовый индекс) char (5) (заметьте, что мы использоняли zipCode, чтобы установить СВЯЗЬ с городом. Это сделано с помощью пяти символов ilpCode. Мы игнорировали дополнительные 4 символа снова для простоты) NOT NULL Да Чтобы проиллюстрировать путь, которым мы формируем наши ограничения, рассмотрим пример таблицы bank (банк). Чтобы создать ограничение для ее столбца name (имя), используем следующий фрагмент: I ALTER TABLE bank ADO CONSTRAINT chkBankSnaeetetringSnotQnpty CHECK stringScheckEmpty(паже) - 1 To же самое можно сделать для поля number (номер) таблицы account (счет): I ALTER TABLE «count ADD CONSTRAINT chkAccountSriumberSstringSriQtEmpty CHECK (dbo. etrin<jSch»ck&qrty (number) = 1)
Предотвращение финансовых сделок с будущими датами В наших технических требованиях отвечено, что нужно предотвратить ввод сделок с будущими датами, поэтому применим е вто! целью ограничешя. Чтобы определить, произошла ли все-так и сделка, мы должны вы по л нить некоторые предварительные операции о нашим и данными типа даты-времени, В частности, нам не нужно хранить время в мши сделках, так что мы должны удалять элементы времени из наших переменных тит даты-времени в ограничениях-проверках, Мы выполним удаление времени из нашего столбца в разделе данной главы, посвященном триггерам, а здесь мы будем просто считать, что эта сделано, Способность проверять, является ли дата будущей или прошлой, является весьма полезным средством, поэтому создадим определяемую пользователем функцию. выполняющую эту операцию: CREATE FUNCTION date?rangecheck ( edateValue datetime, -- первое значение времени edateToCompareAgainst datetime — передача даек для сравнения ) RETURN int AS BEGIN DECLARE greturnVal int IF Mate-Value > tdateToCompareAgainst BEGIN S3” SreturnVal =1 — вудувая лата END ELSE IF MateValue < edateToCompareAgainst BEGIN SET greturnVal • -1 — прошлая дата END ELSE SET SreturnVel - 0 — даты одинаковы. RETURN SreturnVal END GC Снова нам нужно проверить нашу функцию, чтобы быть уверенными, что она работает: — простое условие SELECT dbo.-dateSrangeCheckr 1/1/1959', getdatetM AS [должно бить -1J SELECT dbo.dataSrangeChecktgetdate (), getdateCM № [должно быть 0 J SELECT dbo .dateSraneeChec-M'1/1/2020', getdateO) AS [должно выть 11 Конечно, я приношу извинения, если текущий год больше, чем 2020, и вы все еще используете ату книзу. Пожалуйста измените зту дату, вели необходимо! Далее мы создадим функцию для удаления времени иа величины типа даты-времени: I CREATE FUNCTION dateSremoveTiine I @date datetime ) RETURNS datetime AS BEGIN SET Mate - CAST(dateFart(month, fidatel AS varchar(2)) + '/' * CAST (dateFart(day, Mate) AS varchar(2)) + ’/’ +
ICAST(d*tw?M'&(y«er, Sdete) AS vercbar{4)} RETURN gdate END GO Теперь обратимся к нашей таблице transaction (сделка) и создадим ограничение для столбца date (дата) следующим образом: I ALTER TABLE [transaction] ADD COMSTRAIHT ch:<TransactieaSdateSeat*SnobFuture. — 0 - текущее лата, -1 - означает прошлую дату, 1 - означает eyttyiuys: лату CHECK (dbo.dateSrangeCheck(date, dbo.date$removeTiK«(getdate(>) >- OJ Определение диапазона финансовой сделки Вернемся к таблицам transaction (сделка) и transact i оглу ре (тип сделки); transaction transactions ^attw-ffef a-countid: pnmaryKey (Я<) ИК 1.1) number: stringNumber (АХ 1Л1 date: Datrtnw description; description апиаЛ amnrt signature: payee! d: primaryKey (FK) transacticnTypeid: prims lyKey (FKi autoTimestsmp: аиШТЧтвЯмпр классифицирует transactor/Type_________________ transactionTypefd: primaryKey пгте: name description: description . . requifesSignaturefl; fl requiresPayeeFi: Я aliowPayeeR: fl autoTimestamp: SutoTimeStamp Нам нужно быть уверенными, что все депозиты являются положительными значениями, и что все чеки и электронные изъятия являются отрицательными величинами. Вместо создания ограмичеяия-проверки» основанного ка значении внешнего ключа^ мы можем добавить новый столбец в таблицу transactionType, называемый positiveAmountFl (флаг положительной величины), но ио недостаточно гибко для наших потребностей. Для некоторых элементов можно разрешить отрицательные значения, нуль няи только положительные значения. Мы могли бы также решить, что конкретный тип сделки имей ограниченное максимальное значение, типа автоматического ограничения изъятия в 500 долларов у банкомата. Таким образом, непосредственным способом реализовать это было бы дать возможность пользователю создавать диапазон, добавляя атрибуты minimumAaount (минимальная величина), и maximumAncur. t (максимальная велйяииа>« випей таблице transactionType: transaction f-ansactionid. in! IDENTITY аоеимИ: im HOT ШЦ-tfWAK-l .1) nii-nbsr varcban!2C) NOT NULL (AK1.2) dale: smaildatetime NOT NULL description: varchan( WOO) NOT NULL amount: money NOT NULL a&aftrw: varchan] 1 CO) NOTNUU payeeld: int NOT NULL (FK) tensecttoflT^** int NOT НиЩНО autoTimestamp: smalSdatetime NOT NULL transactionType______ transactionTypekl: int IDENTITY гйлик мвййнй) ЙЯШ descriptor:: wchan( 1ОД NOT tSMl. г^йиа8*^»Я:Ы1ЫОГ№Ю. । гжвйвдаиавя?» NOT NULL
Здесь мы добавляем простое ограничение, чтобы выть уверенными, что minimumAmount яе больше maximumAmount, которые мы ввели: ALTER TABLE tranaactionType ADD minlmiiaAmount тому НОТ NULL ALTER TABLE transactionType ADD maxlntumAmount money HOT NULL — простое ограничение-проверка, wo мшошуы меньше максшогма ALTER TABLE transaetionTj'pe ADD CONSTRAINT chlcTrans*cti©nType$»inAne#taxRengeCh»ck CHECK (minimumAmount <- maxisaimAmount) Теперь мы создадим определяемую пользователем функцию, которая подтвердит, что значение сделки с типом, определяемым таблицей transactionlype, находится внутри диапазона, определяемого значениями столбцов minimuartwount и жаххиипйпюилс: CREATE FUNCTION transactionTv-psivaLidateAnioun-InRange ( etx»n«actioeTyp»Id int, eamount money > RETURNS bit BEGIN — 1 - внутри диапазона, 0 - sue диимиа DECIARE вreturnValue bit IF EXISTS (SELECT * FROM transactionType WHERE яamount NOT BETWEEN minimumArWunt AND maximumAmount AND transact’.onTypeid • etransactionTypeldJ BEGIN SET 0 ret-urnValue = Q BSD ELSE SET (returnValue - 1 END Наконец, мы изменим таблицу transaction и добавим ограничение-проверку для вводимых в таблицу данных: I ALTER TABLE [transaction] ADD CONSTRAINT chkTransaction-Sa»ount₽roperFortraneactionType AS CHECK ;dbo .-ransactionTyceSval-dateAmcuntlnRarige (transactioriTypeld, amount) - 1) Обратите внимание, что ограничение, требующее подпись, ограничения, требующие получателя платежа или допускающие получателя платежа, основянные на флагах в таблице transaction Туре, можно построить примерно таким же образом, так что мы не будем здесь их включать.
Триггеры Удаление времени из дат сделок ПераыЙ триггер, который мы создадим, — триггер INSTEAD OF'— используется для форматирования -даты сделки. Нет никакой причины хранить s дате сделки время, и поэтому мы начнем с шаблона кода триггера и добавим код, чтобы удалить элемент времени из дат, использующих функцию datef removeTime, используемую ранее для ограшиеввя-нроверки; CREATE TRIGGER transactionSinateadOfInsert ON [transaction] INSTEAD OF INSERT AS — Цепи: А. Триггер для операции INSERT, котсрый эапусквется для любого оператора авода языка манипулирования данными (ЯМД} Е. Триггер форматирует столбец date-, удаляя время BEGIN DECIARE trowAffected int, — размещение числа иэменвнных серок BerrorNuniber int, — используется для раамеиения кода — ошибки после выполнения оператора ЯМД Bmag varchar (255), — используется для размещения соовявиия — об ошибке gerrorSeverity varchar 120J, itriggerNarae sysname SET erowsAffected - Mrowcount SET StriggerNaee - object_n«e (f eprocld) — используется для сообщений — не нужво продолжать, вели не» измененних строк IF SrомsAffected = 0 RETORS — выполняем добавление вместо которого создали триггер INSERT INTO [transaction] (accountld, number, date, description, amount, signature, payeeid, transactionTypeld) SELECT accountld, number, dbo,dateSreeaveTiitte{date! AS date, description, amount, signature, payeeid, traneaatieaTypeld FROM INSERTED , и SET ferrorNumber — ввеггог IF вегюгКииЬег О в BEGIN SET fensg - 'Ctatejcat ' + CAST fgerrorKumber AS varchar (10) ) +’ ' возникла при добавлении строк, в ' + ' таблицу transaction.' •МЛЪ-еЯВОЛ. 56000 emsg ROLLBACK TRANSACTION RETUR END ENO Триггер для обновления на 95% имеет тот же код, так что мы оставим его читателю.
Проверка согласования города и почтового индекса В вашей таблице address (адрес}, мы имеем поля cityld (идентификатор города) и zipcode Id (идентификатор почтового индекса), которые япляются опешними ключами к таблицам sipCode (почтовый индекс) и city (город) соответственно. Мы также включили таблицу zipCodeCityRefегелсе (соответствие почтового индекса и города), которая используется, чтобы помочь пользователю выбрать город и штат по почтовому индексу. Мы можем заполнить эту таблицу из БД почтовых ииденюгая, которую можно купить (скажем, в Почтовой службе США). Окончательная цель этой таблицы — проверить соответствие значений city и zipCodeld. Как напоминание, вот физическая модель данных для этик таблиц: ЖЖА(АК1 Л] ват аэ(Эй®ь ийгнот мш.4Ж>да1 “—г— OTtjWwmgr расгюдожеяне I «у «Ш htiDSWTiTY slate Лв*Ь«те<ПТ¥ cede: chart*] ШТ 1Ш (АК1 Л) eutqTmestamg: gmsstasQ ШТ KULL ШТ НШ-1Ж1 Л) mesafflp нот null 5ШШЙ: imWrWAHRg ------- name-rarchar(S01NOTNULHAXt2? определяет ButoTsnestefl'igj ШТЮА zjpCocecaryFtefs'ence esoaH огорода* чеоез zipGssfefefc tot ШТ HUU(HQ WCL1 j cW П ШТЖШлЯС] fAKI^ Рй®уИ:Й NOT NULL as^eTarmssw W2T WAL «чтевыи |®дас®м e neMSw^ Мы можем сделать это, убедившись, что пара cityld и zipCodeld, которая была введена, находится в таблице zipCodeCityReference: CREATE TRIGGER addxesaSafterlnsert ON address AFTER UPDATE AS Цель: Триггер для <onepemot>, котсрый запускается для левой «операции» языка манипулирования данными (лМД1
BEGIN DECLARE growsAffected int, — размещение числа измененных строк веггогМижЬег int, -- используется дня размеиения хсд' — ошибки после выполнения оператор* ЯМД втвд varchar 1255), — используется для размещения ееоввеиия -- об ошиСке gerrorSeverity varcbar (20}, gtriggerName sysnajne SET 6 rowsAffeetea - Mroeeount SET ft riggerNane - objaes^name(Itprocld) — используется алж оввбаений — не нужно йрадодаать, если ват измененных строк IF growsAffected « 0 RETURN DECLARE- 0 nweberOfRawa int SELECT SnunberOfRovs = (SELECT count (*) FROM INSERTED JOIN zipCodeCityReference AS zcr OK zer.cityld - INSBaTED.cityld AND гсг.eipCodeld — INSERTED,г ipCodeId IF BnunberOfRows <> growsftffected BEGIN SET gmsg » CASE WHEN growsAffected - 1 THEN 'Строка, которую вы ввели, содержит ' + 'неправильную пару cityld и zipCodeld,' ELSE 'Одна их строк, которую вы пробовали ввести, * + ’ еаялряжс иапрввильную лару cityld и sipCodeld.’ END RA1SERROR 50000 9мд ROLLBACK TRANSACTION RETURN END END Триггер для добавления на 99в/о имеет тот же код, так что мы оставим его читателю. Распр^менжя сделок н» могут превышать размещенную сумму В атом примере мы рассматриваем суммы распределения сделки для различных видов сделок. Нас будет интересовать noaeallocationAmount (сумма распределения) таблицы transactionAlLocation (распределение сделки):
tnmasQGritt ht ШТ ML Li. numW HUiXtAKW ЙЖ5!ЗДайвШО>Ш tfsrtpiere vaUa^WQDO MJT ШИ штивСгРспеуЫСТЫШ. 8^аш дайЖ12О| ОЖМ P4§*ir tft WT MAX (HQ wttsiWMTK) ЯК нот ши (ng tr^sacwiypeidH HOT NUU (RQ Ж4€еТ Опв^ыеа|^ннуяa тегита ЗЖ^Ш^О^^ТЛ^Й^ШТШХ(₽ЮГАК1 Я U^SSGfeHia: (tO »MX|W1£MW a!iGC3fcnA^cu":MnfflyNO'NUQ wJaTiniSS^mp: WwaBmp ШТ NUU ------------------ , —- --------—------ аа^р^тг^аздйвГ^^а-: in? NOT HULL гелж WEhMBmWT ши des«>5a~; ffidW'WjWTNUU каадифицнрудт w: с яомоиейД Всякий раз, когда пользователь корректирует существующее распределение, мы проводки проверку, чтобы быть увервниымя, что распределено не волыпе денет сделки, фактически существующих. Для этого мы пишем следующий триггер: CREATE TRIGGER transectionAXlocationSAfterInsert ON transactionAllocation AFTER INSERT AS -- Цель: А. Триггер, мюлияший онерами» дойаалеиия, который запускается при каждом оператор» яававлеяиж В. Триггер задииает от распределения Воле» 100% суммы — — — ——-—-= — — — — я₽„.*вв — «ядаявв««1>,«,ж»в4 «-«в.ч* — —. — = = = —J> ВЕ61Ы DECLARE вrowsAffected int, — раэивдание числа измененных строк SetEorSumber int, -- используется для размещения кода — опшвки после выполнения оператора ЯМД Smsg varchar(255), -- используется для размвиения соовшения — оВ ошибке 0errorSeverity v«char(2Q), 0 triggers am» sysnarae SET IrowsAffected - tSrowCourit SET etriggerKama = object_nane(teprocld) — используется для сообщений — не нужно продолжать, если нет измененных строк IF вrowsAffected = О RETURN
- - получение cytaat распределений сделок, на которые воздействует — наев добавление и получение всех сделок, на которые воэдейстдуе» — добавление IF EXISTS { SELECT * FROM ( SELECT transactLonId, awniallocationAmountJ AS amountTotal FROM treasactionAllocation — заметьте, что transactionld расяиряет ваш запрос — ut просмотр всех распределений всея сделок, — чьи распределения мы затронули WHERE transactionld IK (SELECT transactionld FROM INSERTED! GROUP BX transactiotildj AS al 1pcAiMjunta — объединение co сделкой., чтобы получить сумму сделки JOIK [transactionj ON transactionAllocation.transactionld =• ftransaction] . traneactionld — проверка, что сумма сделки не больше, чем — распределенная сумма WHERE [transaction].amount > alloctaounte.amountTotal ) BEGIN SET tmeg » CASE WHEN trowsAffeeted - 1 THEN ' Строка, isoropygi вы ввели., ' + 'делает сделку больше суммк распределения..’ ELSE 'Одна ие строк, которую вы иробаввли вэести, ’ + 'делает сделку вапьше сумьм распределения.' END RAISERROR 50И0 в«ад ROLLBACK TRANSACTI0S RETURN END END Обратите внимание, что мы решили довольно сложную проблему, используя составной запрос, и п ри этом не использовали ни курсоров, ни временных таблиц. Важно формулировать наши запросы в триггерах, используя как можно меньше операторов, чтобы минимизировать возможность возникновения логических ошибок в очень тяжелых для определения местах при формировании сложны* запросов. Всегда будут возникать компромиссы между практичностью и удобочитавмостью/понятноетью. В большинстве систем медленные триггеры будут проблемой, особенно, если мы должны добавить большое количество строк одновременно. Снова, триггер для обновления этой -таблицы (transactionAllocation) и таблицы transaction содержит 95% итого кода, тян что мы не будем его здесь включать. Необязательные правила В нашей системе мы имеем несколько необязательных бизнвс-правил, относительно которых было решено, что их нужно реализовать. Они были определены при первоначальном анализе: □ Пользователь должен получать предупреждение, если попытается забирать слишком много денег — Мы, вероятно, воздали бы хранимую процедуру, которая проверяет баланс счета пользователя до сохранения суммы сделке. Код стороны клиента сравнил вы баланс с суммой сделки, которую пытаются выполнить.
□ Баланс ио счету должен составляться еженедельно — При запуске приложение должно вызвать хранимую процедуру, которая выясняет, когда был составлен последний баланс счета, сравнивает это о текущей датой в выдает напоминание или предупреждение: напоминание могло бы быть послано менеджеру, чтобы уведомить, что баланс был очевидно просрочен. В следующей главе мы рассмотрим лучшие методы создания хранимых процедур. Резюме На этом завершается задача разработки хранения данных для наших ВД. В прошлой главе мы организовали физическое хранение для данных, создавая таблицы. В этой главе мы выполнили следующий шаг и построили схему защиты их. Во время этого процесса были рассмотрены следующие средства, которые дает нам SQL Server, чтобы защитить наши данные от неправильных значений: □ Значения по умолчанию — Хотя они и не кажутся таковыми, но значения по умолчанию могут рассматриваться как средства зшциты данных; они могут использоваться, чтобы автоматически задавать поля, когда назначение поля может быть неочевидно пользователю (и БД добавляет подходящее значение для поля}, Q Ограничения-проверки — Они очень важны для обеспечения того, чтобы наши данные были в пределах спецификации, Мы можем использовать почти любые скалярные функции (определенные пользователем или системные), пока не обеспечим конкретное логическое выражение. □ Триггеры — Триггеры ~ очень важные средства, которые позволяют нам создавать части кода, которые запускаются автоматически при любых операциях INSERT. UPDATE и DELETE, которые выполняются над одной таблицей, □ Хранимые процедурьг/код клиента — Мы просто упомянули эти механизмы защиты данных, потому что хотим быть уверенными, что они используются разумно в обработке бизнес-правил. Во время этого процесса мы осуществили важное использование определенных пользователем функций, являющихся новым средством, которое предлагает нам SQL Server 2000. С ними можно включить многие из используемых алгоритмов, чтобы увеличить и расширить возможности наших значений по умолчанию, ограничений-проверок, триггеров и, как мы выясним в следующей главе, хранимых процедур. Как только мы построили и реализовали набор соответствующих средств защиты данных, можно считать, что данные в вашей БД были проверены. Нам никогда ие нужно будет повторно проверять данные, как только они размещены в нашей ВД, но все же неплохо случайным образом делать проверки только для того, чтобы убедиться, что нет нарушений целостности, которые проскочили мимо нас. Каждое отношение, каждое ограничение-проверка, каждый триггер, который мы изобретем, поможет удостовериться, что это так. В следующей главе мы продолжим рассмотрение некоторых расширенных средств доступа к данным и методов модификации и в особенности использование представлений и хранимых процедур.
Расширенный доступ к данным и методы корректировки Введение В предыдущих главах кы разработали наши БД, организовали размещение и реализовали схемы защиты наших данных. На протяжении этой книги мы предполагали, что читатель относительно знаком с языком Transart-SQL и имеет некоторый опыт написания хранимых процедур и представлений, навыки — которые мы далее разовьем в ходе этой главы. Теперь мы займемся расширенными концепциями доступа, создания, корректировки и удаления данных в нашей OLTP-ВД. Будут обсуждены главные- соображения относительно организации запросов, типа того, как транзакции, временные таблицы, курсоры и значения W1L делают наше кодарование и Солее легким, и более интересным. Мы достаточно глубоки рассмотрим проблемы, связанные е использованием представлений и хранимых процедур и полностью обсудим все "за" и “против" создания приложений, использующих доступ с - помощью хранимых процедур, доступ, использующий нереглахентированные запросы, и их смесь. После обсуждения сущности запросов мы рассмотрим, как можно использовать средства безопасности SQL Server, чтобы ограничить доступ к данных в наших системах. Наконец, мы кратко исследуем, как создание приложений, которые обращаются к данным через разнив серверы, влияет на нашу архитектуру и кодирование.
Одно замечание. В некоторых организациях для архитектора данных выполнение некоторых запач. описанных в этой глие, будет вне круга его обязанностей. Возможно, создание хранимых процедур и представлений будет ближе администратору ВД или программисту БД. С другой стороны, вполне может выть, что архитектор данных является единым во многих лидах. Независимо- от ситуации, я чувствую, что это важно для каждого архитектора данных, чтобы не потерять навыки SQL-кодирования, а полное понимание этих проблем очень полезно для проектирования. Вели вы не являетесь квалифицированным специалистом в SQL, то рискуете спроектировать ВД, которая вызовет у разработчиков много трудностей в процессе ее создания. Рассмотрение запросов В этом разделе мы рассмотрим некоторые из важных проблем, которые нужно понять, чтобы реализовать аффективный доступ к ВД и возможности корректировки. Транзакции, блокировка, значения HULL, курсоры, временные таблицы, хранимые процедуры и представления -— все существенны для архитектуры СУБД. Мы обе улили многие из них ранее в этой книге. Однако в данной главе мы собираемся посмотреть, как эти проблемы затрагивают иодирование наших приложений, а не как от затрагивают создание наших объектов. Транзакции Транзакции играют очень важ^ю-ройь в том, как мы осуществляем доступ к нашим данным. Транзакция — последовательность действий, выиаяненньк. как единственная логически за конченная единица работы. Имеются четыре классических свойства, которые требуется поддерживать в транзакциях, обычно называемые как ACID-тест. Это является аббревиатурой следующих понятий: □ Элементарность (Atomicity) — каждое действие, которое участвует в транзакции, должно Выть выполнено как логическая единица работы, так что каждое действие или выполняется, или не выполняется. □ Непротиворечивость (Consistency) — как только транзакция закончена, система должна быть оставлена в непротиворечивом состоянии, включая физические ресурсы, такие как данные, индексы и смей между страницами данных. □ Изоляция (Isolation) — все корректировки в пределах транзакции должны быть изолированы от других транзакций. При выполнении команд в пределах транзакции никакая другая транзакция не может видеть данные в промежуточном состоянии, □ Живучесть (Durability) — как только транзакция закончилась, все операции, которые выполнялись в транзакции, должны выть сохранены. Транзакция начинается с оператора BEGIN TRANSACTION. Выпаяиеиив BEGIN TRANSACTION помещает нашу связь о ВД в состояние, когда все, что мы будем деяать, не будет сохраняться в постоянном физическом хранилище, пока мы явно не скажем это. Исследуем это в следующем наборе примеров. В первом примере мы начнем транзакцию, вставим строку и затем откатим транзакцию (другими словами, действия транзакции полностью отменяются, и ВД возвращается к своему первоначальному состоянию). Будем работать с таблицей ar cist (артист) из предыдущей главы и начнем с получения основного списка артистов в нашей таблице:
I SELECT artistld, name FROM artist Мы видим, что имеются три сохраненных артиста (имейте в виду» что значения столбца artistld могут меняться в зависимости от того, сколько значений вы ввели, плюс то, сколько произошло ошибок): artistld name 27 jethro tul 1 the beaties 2 the who Далее выполним следующий набор команд: I BEGIN TRANSACTION — начало транзакции — ваол двух записей INSERT INTO artist (name> VALUES ('raariah carey') INSERT INTO artist (name) VALUES ('britney spears'} -- проверка, что они в ВД SELECT artistld, павж FROM artist Этот фрагмент возвратит результат: artistld name 37 britney spears 27 36 1 2 jethro toll mariah carey the beaties the who Теперь мм решаем, что на самам деле не хотим иметь эти записи в нашей ВД и поэтому откатываем транзакцию, за которой, однако, следует другая проверка строк в таблице: Нак мы обсудим позже, иметь транзакцию, охватывающую несколько действий, — не лучший вариант, на я делаю то здесь, чтобы показать вам значения в таблице. ROLLBACK TRANSACTION £ SELECT artistld, папе FROM artist Теперь строки исчезли, как будто их никогда не было. artistld name 27 1 2 jethro tull the beattes the who
Формально говоря, строки физически существовали, но SQL Server отметил их так, что мы можем или зафиксировать их как группу, или, как в нашем случае, не фиксировать их. Транзакции могут выть вложены так, что для каждого oneparopaBEGlN transaction, который мы выполняем над ВД, мы должны выполнить один оператор COMMIT TRANSACTION. С другой стороны, оператор ROLLBACK TRANSACTION отменяет неограниченное число транзакций. Мы можем определить, сколько транзакций было начато, исследуя глобальную переменную eetranaount. В следующем примере мы покажем влияние выполнения oneparopoBBEGIN TRANSACTION и COMMIT TRANSACTION, отслеживая значение Otrancount до и после команд транзакции: SELECT eatxancount AS zero BEGIN TRANSACTION SELECT 80trenco«nt AS one BEGIN TRANSACTION SELECT 9Btrancount AS two BEGIN TRANSACTION SELECT ggtrancount AS three COMMIT TRANSACTION SELECT eetrancount AS two COMMIT TRANSACTION SELECT ggt".г..'ount AS one COMMIT TRANSACTION SELECT ggtEancount AS zero Этот фрагмент возвратит следующий результат. Заметьте, что ои увеличивается от нуля до трех, а затем уменьшается до нуля. zero О one 1 two 2 three 3 two 2 one 1
zero О Мы должны завершить каждую транзакцию, иначе ресурсы будут заперты от других наших транзакций на основе свойства изолядан ACID-теста. Однако, как мы сказали ранее, оператор ROLLBACK отменяет все операторы BEGIN TRANSACTION срезу: I SELECT eetrancount AS one BEGIN TRANSACTION SELECT ggtrancaunt AS two BEGIN TRANSACTION SELECT ggtrar.count AS three BEGIN TRANSACTION SELECT Sitranco-unt AS four ROLLBACK TRANSACTION SELECT ggtrancount A5 zero Этот фрагмент возвратит: one О two 1 three 2 four 3 zero 0 Это — большое достоинство, а также и проблема. Для команд запуска у вас, вероятно, будет несколько операторов C0M4IT после оператора отката, потому что вы будете обычно использовать логику, которая утверждает: если что-то работает, завершайте его; если не работает, выполните откат. После отката мы должны сообщить каждому следующему оператору, что не собираемся выполнять его. С этой целью SQL Server дает нам дополнительную возможность для транзакций, которая позволяет выборочно откатывать некоторые части транзакции. Выборочный откат Здесь используется оператор SAVE TRANSACTION. Вы можете задать имя точке сохранения (на основе обычных критериев для идентификаторов, хотя существенными являются только первые 32 символа) следующим образом: SAVE TRANSACTIOH <1®«я_точки_сохранения>
В атом случае, если вы решили, что часть транзакции выполнена неверно, то можете откатить только часть ее, но не вею, выполняя оператор наподобие этого: § ROLLBACK TRAKSACTIOH <имя_то«ки_сохранеиия> Обратите внимание, что это отличается от BEGIN TRANSACTION <имя_транзакцил>. Итак, в нашем заключительном примере, посвященном транзакциям, мы используем команду SAVE TRANSACTION, чтобы выборочно откатить одну из команд, которую мы выполняем, а другую — нет: Begin TRANSACTION — начало транзакции — помешаем, две записи INSERT INTO artist (name} VALDES ('moody blues'’> SAVE TANSACTION britney INSERT INTO artist (name) VALUES t'b-ritney apeara') — проверка, что они в БД SELECT artistld, name FROM artist который дает результат: artistld name 45 27 44 1 2 britney spears jettire toil moody blues the beattes the who Далее мы выполним откат к созданной нами точке сохранения britney, завершим основную транзакцию и выберем наши строки, чтобы посмотреть состояние таблицы art I ROLLBACK TRANSACTION britney COMMIT TRANSACTION SELECT artistld, name FROM artist Мы видам, что этот фрагмент Полжем откатить строку "britney spears", но не строку "moody blues". artistld name 27 44 1 jethro toll moody blues the beattes
Мы будем использовать механизм SAVE TRANSACTION весьма часто при создании транзакций, Ои позволяет осуществлять откат только части транзакции и решить, какое действие выполнить. Это Судет особенно важно, когда мы имеем хранимые процедуры, так как хранимая процедура ие позволяет изменять значение OGtrancount е помощью своих операций. Область действия транзакций Пакет (batch) представляет собой логическую группу операторов Т-SQL, которые анализируются, а затем выполняются совместно. Мы используем оператор GO, чтобы разделить код на пакеты; GO не является командой Т-SQL, а только ключевым словом, раетознаваетым утилитами команд SQL Server, наподобие Query Analyzer. Транзакция имеет область действия на все соединение с SQL Server и может теоретически охватывать несколько пакетов операторов, пересылаемых между клиентом-и сервером. Обратите внимание, что соединение ие те же самое, что пользователь. Пользователь «ожег иметь много соединений с сервером, и фактически он может выть вынужден ждать завершения -транзакции в одном из своих соединений, чтобы продолжить работу Одна транзакция (вли яавор вложенных транзакций) должна быть закончена, прежде чем следующая может начаться, Как SQL Server может удостовериться, что, если связь разорвана до того, как завершилась транзакция, произошел откат к ее первоначальному состоянию? Как SQL Server может удостовериться, что транзакции ве накладываются друг на друга, нарушая таким образом требование изоляции ACID-теста? В следующем разделе мы рассмотрим механизмы блокировки, которые SQL Server использует, чтобы отслеживать работу транзакций в ВД. Блокировка Блокировка в SQL Sarver несколько напоминает знак занятости, который вы видите на двери ванной комнаты. Она сообщает внешнему миру, что что-то происходит внутри, и каждый другой должен будет ожидать. Блокировка играет важную роль в обеспечении правильной работы транзакций и подчиняется АСШ-правилам. Откат транзакции может выть опасен, если другая транзакция уже начала работать с тем же самым набором данных. Эта ситуация, когда несколько пользователей пытаются обратиться к ресурсам в одно и то же время, обычно называется параллелизмом. В этом разделе- мы дадим основной краткий обзор того, как внутренне реализуется блокировка (в противоположность нашему использованию оптимистической блокировки, рассмотренной в главе 10) ж как она предотвращает искажение данных, если иесиоиько процессов пытаются обратиться к данным одновременно. Типы блокировок SQL Server внутренне реализует схему блокировок, которая подволяет пометить транзакцию или операцию, что она связана е данным ресурсом или коллекцией ресурсен. Метка указывает другим процессам, что ресурс заблокирован SQL Server предотвращает явиую установку и очистку блокировок, так что ресурс с такой меткой защищен. В зависимости от уровня использования могут быть заблокированы следующие ресурсы (типы блокировок перечислены в парадке уменьшения детализации): О Строка — отдельная строка в таблице. □ Ключ иди диапазон ключа — ключ или диапазон ключа в индексе, □ Страница — страница структуры в 8 Кб, которая содержит ивдеке или данные.
□ Таблица — полная таблица, то есть, все строки и кяочи индекса. Q ВД — полная ВД (обычно используется только при изменении схемы ВД) Обратите внимание, что имеется также блокировка экстента (экстент обычно используется при распределении памяти в ВД и имеет размер в восемь страниц no S Кб). Начальный тип используемой блокировки определяется оптимизатором SQL Server, который также решает, когда переходить от одного типа блокировки к другому, чтобы экономить память и увеличить производительность. Вообще говоря, оптимизатор выберет наименьшую блокировку — тип блокировки строки, если это возможно, но если возншсиет вероятность блокировки всех иля части строк, может быть выбран тип блокировки таблицы. Используя указания оптимизатору в наших запросах, мы можем до некоторой степени регулировать типы используемых блокировок. Это будет кратко рассмотрено. Способы блокировки Конечно, системному архитектору полезно понимать, как SQL Server выбирает степень иснользуежой детализации уровня. Сначала мы опишем различные существующие способы блокировки: □ Совместные блокировки — используются, когда вы просто хотите просмотреть данные. Несколько соединений могут иметь совместные блокировки одного и того же ресурса. □ Блокировки обновления — они позволяют нам указать, что мы планируем обновить данные, но пока не готовы это делать, например, если мы все еще обрабатываем выражение WHERE, чтобы определить, какие строки нужно обновить. Мы можем все еще иметь совместные блокировки таблицы, и другие соединения могут быть освобождены в течение периода времени, пока мы асе еще готовимся к обновлению. □ Эксклюзивные блокировки — они дают исключительный доступ к ресурсу. Никакая другая связь не может иметь доступ вообще. Они часто используются после того, как физический ресурс был изменен в пределах транзакции. О Направленные блокировки — они передают информацию другим процессам, которые имеют или собираются создавать блокировки некоторого типа. Имеются три типа направленных блокировок: направленные совместные, направленные эксклюзивные И совместные с направленными эксклюзивными (чтение с намерением сделать изменения). Для любого, кто когда-либо был около водолаза, направленные блокировки аналогичны бакенем. которые водолазы помещают на поверхности воды, чтобы отметить свое положение. Поскольку мы видим бакен, то знаем, что имеется деятельность, протекающая под водой. SQL Server видит эти маркеры на объектах и знает, что если, например, направленная совместная блокировка расположена на таблице, то невозможно получить исключительную блокировку для этой таблицы, поскольку другие соединения уже используют ресурсы. Наряду с предыдущим списком ресурсов, которые могут быть заблокированы, имеется несколько дополнительных способов, которые мы упомянем мимоходом, но которые находятся за пределами возможностей славы. Блокировки схемы используются, когда изменяется структура БД, перекомпилируется хранимая процедура или что-нибудь еще изменяется в физической структуре БД. Блокировки массового обновления используются при выполнении массовых корректировок. Для дополнительной информации см. SQL Server 20Q0 Book» Online.
Итак, мы теперь знаем, что имеются блокировки различных уровней процесса, и каждый раз, когда мы делаем что-нибудь в SQL Server, устанавливаются метки, сообщающие другим процессам, что мы работаем с ресурсом. Это должно заставить их ожидать, пока мы не закончим использовать ресурс, вели они должны выполнять задачу, которая является несовместимой с тем, что мы.в навтйищеевремя делаем, — например, пробуя просматривать запись, которую мы изменяем, или пытаясь удалить запись, которую мы в настоящее время выбрали для чтения. Уровни изоляции Блокировки устанавливаются на время выполнения элементарных операций, которые мы попросили, чтобы SQL Server выполнил. Рассмотрим, например, случай, когда мы начинаем транзакцию е таблицей, называемой tableX (таблица X): £ SELECT * FROM dbc .tabieX Блокировки (в этом случае совместные блокировки) устанавливаются для этого ресурса на время, определяемое так называемым уровнем изоляции. SQL Server обеспечивает следующие четыре уровня изоляции, перечисленные здесь от наименее до наиболее ограничительного: □ Свободное чтение (Read Uncommitted) — игнорирует все блокировки и не формирует блокировки. При свободно читаемом уровне изоляции мы можем просматривать данные при элементарных операциях, которые не были пока закончены, и которые можно откатить. Обратите внимание, что никаких корректировок на этом уровне изоляции фактически не происходит; если вы изменяете строку в пределах транзакции, вы устанавливаете блокировки, которые предотвращают другие процессы от несовместимых изменений. Это очень опасная вещь — выполнять такую изоляцию в общем случае. Так как мы можем при этом просматривать свободные транзакции, никогда не ясно, насколько ценно то, что мы читали. Однако такой вариант может использоваться во время формирования отчетов, если при этом можно достигнуть границ ошибок; это может позволить читать данные, которые не удовлетворяют всем установленным ограничениям и которые могут исчезнуть после чтения. □ Фиксированное чтение (Bead Committed) — это намного чаще используемый уровень изоляции, поскольку он обеспечивает разумный баланс между безопасностью и параллелизмом. Мы не можем видеть свободные данные, хотя и допускаем неповторяемое чтение (условие, при котором если мы читаем набор строк, то ожидаем, что строки будут существовать иа всем протяжении транзакции) и "невидшиие" строки, являющиеся новыми записями, которые создаются другими пользователями в то время как мы находимся в нашей транзакции. Обычно, это — лучший выбор для уровня изоляции и фактически используется по умолчанию в SQL Server. □ Повторяемое чтение (Repeatable Read) — повторяемое чтение гарантирует, что если пользователь должен выполнить один и тот же оператор SELECT дважды и при этом он не изменил данные между этими двумя операциями, то он получит точно те же самые данные дважды. Неповторяемое чтение не может гарантировать этого, если другой параллельный пользователь может изменить данные.
Как вы могли предполагать, повторяемое чтение защищает уровень фиксированной изоляции и предотвряпщет иеипвторяемое чтение, помещая блокировки на все данные, которые используются в запросе, предотвращая другие связи от изменения денных, которые вы уже читали. Когда жы выполняете запрос второй раз, он возвратит, по крайней мере, тот же самый набор строк, как это выло в первый раз. Однако это не предохраняет ст "невидимых” строк. □ Упорядочиваемый (Serializable) — этот уровень изоляции размещает блокировки диапазонов на индексах и наборах данных, предотвращая пользователей от создания любых "невидимых” строк. Чтобы проиллюстрировать то, что я сказал, рассмотрим пример того, что произойдет, если мы возьмем уровень изоляции е фиксированным чтением, используемый по умолчанию. Сначала мы создадим таблицу, которую будем использовать для испытания, и добавим некоторые записи: CREATE TABLE testXsolationLevel ( tettlsoletioftLevelld int IDEKTJTT, value varchar(10) **'сг,»Т into dbo, testlsolatioalevel(value) is ('Значение 1') Г INTO dbo.testleolationtevel(value) g VALUES ('Значение 2'} Зёесь создается таблица testzsolatiohbevel (испытание уровня изоляции) с влумя столбцами: автоинкрементный столбец testlsolationLevelltl (идентификатор строки таблицы "испытание уровня изоляции") типа int и столбец value (значение) типа varchar (10). Далее е эту таблицу помещаются две записи, у которых столбец value имеет значения "Значение 1" и "Значение 2". Далее мы можем выполнить следующие команды в одном соединении с SQL Server: I SET TRANSACTION ISOLATION LEVEL READ COMMITTED -- задается только для целей иллюетрацвд». Если ъал не устаиов»ш это — для соединения, то тем не менее будем находиться в режима — фиксироааннсго чтения, поскольку эта значение по умолчанию BEGIN TRANSACTION SELECT * FROM dbo,teetlaolationLevel и увидим следующий результат: testisolationLeveiid value 1 Значение 1 Значение 2 Используя другое соединение, яы г одним следующие операции: I DELETE FROM dbo.teatlsolatiunLevel where xestlsolationLevelld - 1
Затеи верявмся к нашему первому соедииеиию, которое все еще открыто, и внутри транзакции выполним операцию: SELECT » FROM dbo.zeatXsoIaticnLeveX COMMIT TRANSACTION и у нас нет проблем о удалением отроки: testlsolationLevel'd value 2 Значение 2 Это было неповторяемое чтение. Затем мы удалим таблицу, заново инициализируем ее и повторно выполним наш небольшой тест, изменив уровень изоляции: Ц SET TRANSACTION ISOLATION LEVEL REFEATABLE READ На сей раз, когда мы пробуем выполнить оператор DELETE во вторам соединении, он не будет завершеи, пома не будет закрыто наше первое соединение. Второе соединение будет заблокировано первым соединением и не сможет выполнять свои команды. Следовательно, наш второй оператор SELECT возвратит, по крайней мере, те же самые значения — другими слонами» это — повторяемое чтение. Но мы все еще должны рассматривать проблему ‘'невидимок" (то есть "невидимых" строк). Мы предотвратили любые удаления строк, но что касается добавления строк? Из навваиия "повторяемое чтение* мы могли вы полагать, что защищены от этой ситуации. Однако давайте повторим нашу ситуацию снова. Мы удалим таблицу и заново создадим ев, поместив те же самые две строки. Затем выполним первоначальную транзакцию с первым соединением: ISET TRANSACTION ISOLATION LEVEL READ COMMITTED BEGIN TRANSACTION SELECT * FROM dbo.~estI solationLevel (Здесь, повидимому, сделана ошибка, и требуется написать Ц SET TRANSACTION ISOLATION LEVEL REPEATABLE READ кан следует из последующих рассуждений. Прим, переев „.и снова увидим следующие результаты: testlsoladonLeveild value 1 Значение 1 2 Значение 2 Во втором соединении мы выполним следующую команду: INSERT INTO dbo. testlsulationLe’sel (value) VALUES (Значение 3')
Эта операция не блокируется, как выло с операцией DELETE, поскольку это допустимо. Теперь, если мы выполняем следующую команду в первом, еоедивеиия, как прежде: I SELECT * FROM dbo.cestlsoletionbevel COMMIT TRANSACTION мы получим дополнительную строку: testisolationLevelld value 1 Значение 1 2 Значение 2 3 Значение 3 Теперь мы имеем повторяемое чтение, во также имеем и "невидимую” строку. Наконец, если мы вернемся и запустим этот пример в последний раа, но определим уровень изоляции SERIALIZABLE (уиорядочивае.чый), оператор INSERT в этом примере будет ожидать завершения соединения, И» наших примеров может показаться, что упорядочиваемый уровень изоляции является лучшим вариантом, если мы хотам запретить другим пояыователши обращаться к нашим -аиным в то время, когда мы используем их. Исли дашпле изменяются, используя транзакцию, то вероятнее всего желательно, чтобы данная: ситуация существовала, пока не закончит®! зсе действия. Всякий раз, когда мы изменяем данные, SQL Server размещает эксклюзивные блокировки в ресурсы, которые мы изменяем. Вели другой пользователь пробует подучить этот конкретмый ресурс, он должен будет ожидать. Однако яее другие ресурсы, которые используются как часть транзакции, также будут заблокированы, и параллельный пользователь не может обращаться ни к какому из этих ресурсов, когда они используются в транзакции. Когда уровень изоляции увеличивается, SQL Server должен разместить и поддерживать более серьезные блокировки ресурсов в процессе: □ Свободное чтение (Read Uncommitted) — никакие блокировки не устанавливаются и никакие не учитываются, даже эксклюзивные блокировки. □ Фиксированное чтение (Bead Committed) — устанавливаются совмесише блокировки во время просмотра ресурса, ио как только процесс заканчивается, они освобождаются, □ Повторяемое чтение (Repeatable Read) — устанавливаются те же самые совместные блокировки, однако они не освобождаются до конца транзакции. □ Упорядочиваемый (Serializable) — все диапазоны данных (включая целые таблицы), которые могут встретиться при любом выполняемом нами чтении, блокируются, и никакой внешний пользователь не может делать никаких изменений в этих данных. Одно примечание. При использовании любого уровня изоляции, отличного от упорядочиваемого, мы оставляем севе маленькую лазейку в целостности данных. Рассмотрим, что произойдет в триггерах и ограничениях-проверках, которые используют функции для доступа к другим таблицам, чтобы посмотреть, встречались ли ранее условия для сохранения записи. В зависимости от нашего уровня изоляции, мы могли бы проверить, что существует некоторое условие, а десятью миллисекундами позже другой пользователь мог бы изменить данные в нашей системе, при этом данные находятся в пределах спецификаций. Другими десятью миллисекундами позже мы можем передать на диск теперь уже неправильные данные.
Это дополнительное свидетельство, подтверждающее наше раннее утверждение, что упорядочиваемый уровень наиболее подходящий для всех ваших транзакций. Однако мы должны объяснить причину, почему фиксированное чтение является уровнем изоляции по умолчанию дли SQL Server. Он имеет все, чтобы сбалансировать потенциально противоречивые проблемы последовательности и параллелизма. Использование упорядочиваемого уровня изоляции может запретить другам пользователям получать данные, в которых они нуждаются, следоэательно. сильно сокращая параллелизм, особенно, когда вы вмште много пользователей, использующих одни и те же ресурсы. Использование фиксированного чтения оставляет щель в нашей защите в течение чрезвычайно маленького периода времени (обычно порядаа миллисекунд), с минимальным риском. Рекомендуется, чтобы упорядочиваемый уровень и повторяемое чтение использовались только тогда, когда они абсолютно необходимы. Примером такого случая могла бы быть банковская или финансовая система, где даже самая маленькая возможная ошибка в счете пользователя могла бы вызвать реальные проблемы. Другая рекомендация заключается в том, что все транзакции должны быть как можно меньше, чтобы..сохранять блокировки как можно короче. Это влечет за собой попытку держать транзакции в едином пакете. Любые заблокированные ресурсы остаются такими, пока SQL Server ожидает завершения вашей работы. Очевидно, что работа ухудшается, если вы дольше ожидаете и выше уровень изоляции. SQL Server будет всегда стараться закончить любой пакет, который он начал выполнять, Если мы попытаемся разорвать соединение в середине открытой транзакции, соединение не будет рааорваяо немедленно, нарушая дальнейшую работу. Указания оптимизатору Сейчас мы кратко упомянем об одной возможности на уровне таблицы — указаниях оптимизатору. Если нал не нужно полное соединение или операция использования конкретного уровня изоляции, но мы просто хотим каким-то образом использовать в операторе таблицу, то можем определить уровень изоляции как указание оптимизатору. I SELECT <имя_полл_1>, <хмя -оля С>, ... , <>•>.:я_йоля_Н> FROM <имя_тавииаы_1> WITH (optimizerHintl, cptimizerHint2, optimizerHintX! WHERE «условие» Здесь opt inti zerHintK — K-e указание оптимизатору. Предположим, что мы имеем, например, таблицу customer (заказчик) и таблицу invoice (счет-фактура), которая имеет внешний ключ к таблице customer, i SELECT invoice,date AS invoiceDace FROM dbo.customer With (READONCOMMITTEB) JOIK dbo.invoice OH customer. custbsier Id invoice.customerld В этом экземпляре нет никаких общих блокировок, помещенных в таблицу, customer, ио они имеются в таблице invoice, поскольку к ее ресурсам -обращаются. Имеются указания оптимизатору для каждого значения уровня изоляции, а также и «которые другие, чтобы сформировать некоторые виды блокировок: □ READUKCOMMITTED (также известный как HOLOC К) — никакие блокировки не устанавливаются и неиспользуются,
□ REA0C0M4ITTEP — попользует все блокировки, ио блокировки не сохраняются, как только завершаетяя работа е ними (используется да умолчанию). □ REPEATABLEREAD — никогда не удаляются блокировки, раз они были установлены. □ SERIALI ZABLE (также известный как HQ1DLOCK) — никогда не освобождает блокировки, пока транзакция не завершит работу, и блокирует все другие операторы корректировки строк, которые могли бы когда-либо соответствовать любому оператору WHERE, которым ограничивает выборку данных указанной таблице. Q READPAST — используется только для операторов SELECT; он сообщает процессору запросов, чтобы он переекакивял черев блокируемые строки и перемещался к неблокируемым, Игнорирует только блокировки строк, □ ROWLOCK — инициирует внутренний механизм блокировки, выбранный для операций блокировки на уровне строк, даже если бы оптимизатор выбрал вы болев мелкую блокировку. □ TABLOCK — инициирует блокировку таблицы, используемую даже тогда, когда обычно используется более мелкая блокировка. □ TABLDCKX — то же, что и TABLOCK, но ижользувэтея акеклюзивные блокировки вместо любого другого типа блокировки, который мог вы быть выбран. □ tjPDLOCK — инициирует использование блокировки овиовлевиж вместо совместной блокировки. В то время как каждое иа этих указаний оптимизатору имеет свое применение и может быть полезно, их следует использовать с чрезвычайной осторожностью, и в большинстве случаев будет лучше использовать уровни изоляции вместо попытки непосредственно управлять типами и способами блокировки. Тупиковые ситуации Тупиковая ситуация — ситуация, которая происходит, когда каждое из двух (или большего числа) соединений блокирует ресурсы, в которых нуждается другое соединение. Для иллюстрации предположим, что мы имеем два соединения — А и В — которые выполняют команды, чтобы модернизировать две таблицы — tableA (таблица А) и tableB (таблица В). Проблема возникает, потому что соединение А стчала корректирует tableA, а затем tables, в то время как соединение В корректирует те же самые таблицы, но в обратном порядке. Соединение А Соединение В Соединение с таблицей А Таблица А блокирована Корректировка выполнена Попытка связаться с таблицей В Нлокировка таблицы В Соединение с таблицей В Таблица В блокирована Корректировке выполнена Попытка связаться е таблицей А Влокировка таблицы А Так как нет выражения WHERE, будет блокироваться ноя таблица, чтобы выполнить корректировку, так что блокировка будет применяться к tableA в то время, когда соединение использует ее. То же самое произойдет с комаидай корректировки всех строк tableB соединения В. Когда эти два соединения пытаются выполнить свои вторые операторы UPDATE, они оба, блокируются — создается тупиковая ситуация. Тогда SQL Server объявляет одно на соединений жертвой тупиковой ситуации и освобождает другое соединение, откатывая блокировки, которые выли выполнены на ресурсе.
Чтобы определить, какое соединение завершается в тупике, SQL Server выбирает соединение, которое проще откатить назад. Однако имеются настройки, которые вы можете использовать, чтобы поднять приоритет тупика соединений, помогая SQL Server выбрать менее важную связь, чтобы ее удалить. Следующая команда SET установит приоритет тупика соединений/ равный LOW (меньший) иди NOMttL (нормальный). NORMAL — значение, используемое по умолчанию, а настройка LOW для соединения означает, что наиболее вероятно оно будет жертвой тупика и будет удалено. II SET DEADLOCK_^PRIORITY [LOW I NORMAL] Тупики раздражают, но они довольно редки, ж если вы ожидаете их появление и создаете код, чтобы избежать их, они могут выть легко обработаны. Вы можете просто закодировать ваше приложение, чтобы искать сообщение об ошибке, связанное с возникновением тупика (1206), я сделать так, чтобы повторно выполнить команду, при этом маловероятно, что вы получите тупиковую ситуацию второй раз подряд. Обратите внимание, что эту ситуацию не следует путать е блокировкой, которую мы обсудили-ранее, Тупиковые ситуации заставляют одна из соединений отключаться и сформировать ошибку; блокированные соединения, ожидают время, определяемое значением WCH_TIMEOUT (время отката блокировки), и, затем откатывают связь. Вы можете- установить зто свойство LOCK_TIMEOUT, выполняя оператор: Ц SET LOCK_TIMEOOT <аремч_в_^миллисекундах> Ключ к уменьшению тупиковых ситуаций опять-таки в коротких транзакциях. Чем короче транзакция, тем меньше шаиеов, что будет блокирование или получение тупиковой ситуации другими транзакциями. Одно предложение, которое постоянно упоминается: всегда в ваших транзакциях организовывать доступ к объектам в одном и том же порядке, может выть, в алфавитном порядке. Конечно, такие предложения будут работать только в том случае, если каждый соблюдает это правило. Основные рекомендации Имеются две очень важных рекомендации для любых клиентов, которые будут использовать наши данные. Избегайте долгой работы транзакций Мы дстречались с этим советом уже несколько раз. Однако, что означает долга* транзакция? Долго работающая транзакция может занимать половину секунды, одну минуту, один час или даже день. Что здесь надо иметь в виду, так это то, что мы минимизируем транзакции, которые взаимодействуют е нашими пользователями, обеспечивая их доступ к данным. Вместо того чтобы загружать значения из внешнего источника непосредственна в таблицу наших пользователей, может оказаться лучше загрузить строки в новую промежуточную таблицу, а затем вставлять строки в таблицу пользователей в течение непиковых часов. Непроизводительные издержки’при этом могут быть гораздо мень-пе, чем копирование большой массы данных непосредственно в таблицу.
В особых случаях, когда ие-удается реально выполнять траизаицию за короткое время, вы должны будете или перепроектировать процедуру, или смириться е этим. Когда длительность транзакций: занимает пару оекуид или меньше того, не так уж много программных возможностей, чтобы уменьшить ату неприятность. Если эта продолжительность нас не устраивает, то может помочь настройка параметров. Транзакции не должны шсгдфт за границы пакета Если вы посылаете на сервер несколько пакетов команд, лучше ие начинать транзакцию в одном пакете, а заканчивать ее в другом. Следуя этой важной, рекомевдвции, мы можем сильно уменьшить число некоторых наиболее неприятных ситуаций в работе, которые могут произойти. Рассмотрим следующий пример, На рисунке мы имеем две команды, которые модернизируют tableA (таблица А), а затем t-аЫев (таблица-В). Выло определено, что нужно выполнить эти команды в транзакции, и мы создали с этой целью аккуратный небольшой набор методов. Вызовем методbe<jin_transaction (J, который соединяется с сервером и начинает транзакцию, Затем он повылает команду, чтобы скорректировать tableA, а за ней команду для корректировки tableB, но где-то в течение последнего вызова локальная сеть разрывается: SQL Server не будет знать, что ваша связь была разъединена, так что он продолжает сохранять соединение открытым, ожидая, когда вы передадите команду С0Ю41Т или организуете откат, Однако, поскольку вы теперь отошли от транзакции, ни COMMIT, ни откат не произойдет, нова SQL Server не поймет, что связь была нарушена ввиду ее бездеятельности. Наши данные будут правильными только тогда, когда транзакция создает блокировки обновления, чтобы защитить их. Единственная проблема заключается в том, что в зависимости от фактического оператора WHERE деблокировка строки, строк или даже всей таблицы будет затруднена. Следовательно, когда другой пользователь собирается просто просмотреть запись, в tableA, ои/она вынужден ожидать или нажать кнопку Stop (остановка) на броузере (browser). Ответом на эту проблему может -быть передача этих команд в единственном пакете, который выполняет все одновременно. Например: BEGIN TRANSACTION Q UPDATE tableA
UPDATE tai' ~ СОМЯТ TRJ | GO В этом случае, если SQL Server получает команды и начинает выполнять их, то или будут выполнены все операции, или ни одна из них. Мы же будем ааверпияь работу с какими-либо заблокированными ресурсами. Так с какой стати пользователь рассматривает посылку транзакций в нескольких пакетах? Здесь пара причин: □ Наблюдаемая скорость — как только каждый оператор завершается, можно скорректировать индикатор выполнения и сообщить Еольаоааталю, что tableA действительно была обновлена, теперь я перемещаюсь к tableB и т. д. □ Вее это легко получить, даже не зная об этом, особенно иепольауя средства типа ADO, Рассмотрим следующий пример кода АЛО, взятый из SQL Server Books Online, раздел Performing Transaction in ADO (Выполнение транзакций в ADO): Dim cn As New ADOB.Connection Dim rs As New ADOB.Recordset r Открытие соединения. СП.Open * Открытие таблицы titles (заголовки). rs.Open "SELECT * FROM titles", cn, adOpenDynamic, adtockPessimistic 1 Начало транзакции, rs .MoveFirst. cn.Beginlrans ‘ Пользователь просматривает набор записей, выполняя изменения. ' Вопрос, собирается ли пользователь сохранить все сделанные изменения. If MsgBox(“Сохранить все изменения?", vbYesNo) - vbbes Then cn.CcmmitTrans Else cn.RollbackTrans Ena if Этот код будет делать точно то же самое, что мы обсуждали ранее, но в намного более приятном оформлении. Выполняя в соединении метод BeginTxans, серверу посылается команда BEGIN TRANSACTION в одном пакете. Для каждого контакта е записью в цикле (который не закодирован, а только упоминается) оператор корректировки данных будет послан к серверу. Наконец, если все идет хорошо в коде, будут вызываться метода! Сопип! titans или Rollback!cans, посылая команду СЭИТТ TRANSACTION (илв ROLLBACK TRANSACTION — Прим.. nepee.;. Вели внутри возникнет любая ошибка или даже разрыв соединения, то пользователи таблицы titles будут долгое время ожидать. Обратите внимание также, что вызываемая блокировкаadLockFessimistic будет, вероятно, реализовывать уровень изоляции READCOMMITTED (фиксированное чтение) яяй, может быть даже SERIALIZABLE (упорядочиваемый), так что никакой другой пользователь не может касаться этих отрок, пока данный пользователь работает с ними в этом цикле. Простой пример кода показывает, как работают несколько методов в соединении о объектом, во его не лучший пример при использовании многопользовательских программ.
Одаако если в этой ситуации мы просто размещаем каждый оператор вместо того, чтобы посылать их к SQL Server по одному, мы могли бы выполнить пакет операторов, как только пользователь закончит его формировать. Пользователю, вероятно, придется ждать немного дольше, когда закончится каждая операция, по сравнению в тем, когда каждый шаг выполняется отдельно. Фактически, на все будет затрачено меньшее количество времени, во пользователю будет казаться, что ничто ве происходит, и время будет представляться более дивим. Однако в атом случае, вели связь Судет прервана после того, как сервер получит для работы пакет, мы можем убедиться, что все изменения пользователя будут присутствовать, и все блокировки, используемые для обеспечения непротиворечивости, будут немедленно освобождены. Следуя очень простому правилу хранения транзакций в пределах одного пакета, мы не будем оставлять транзакции подвешенными, поскольку SQL Server будет знать, что все требуемое для транзакции готово до того, как будет выполнена вданственная команда. Вели мы разорвем соединение, нам гарантируют, что или все, или ия одной из наших команд не будет выполнено, и мы не будем затрагивать работу никакого другого пользователя дольше, чем необходимо. Мы возвратимся к теме составления пакета команд позже в этой главе, когда рассмотрим хранимые процедуры. Основные проблемы кодирования Любое обсуждение доступа к данным и методов корректировки, использующих запросы, не было бы голно без рассмотрения трех особенностей, которые больше всего затрагивают работу SQL Server: □ временные таблицы; □ курсоры; □ значения NULL. Мы рассмотрим эти темы достаточно глубоко, чтобы вы могли в ними работать эффективно и избегать потенциальных проблем во время работы, которые могут возникнуть ив-за их неправильного использования. Временные таблицы Временные таблицы Выли кратко упомянуты ранее в книге, когда мы обсуждали создание таблиц. Они подобны постоянным таблицам, но удаляются на системы, когда перестают использоваться. Имеются два различных вида таких таблиц: О Локальные временные таблицы (чьи имена предваряются символом #), которые используются для соединений с SQL Server. □ Глобальные временные таблицы (имена предваряются символами ♦#), которые могут рассматриваться любой связью и любым пользователем в системе. Они не перестают использоваться, пока соединение, которое их создало, ие будет разорвано,и любые другие связи, которые используют эти таблицы, не прекратят яйрятатьея к ним. Временные таблицы позволяют иам создавай» наше собственное временное хранилище и добавлять в них некоторые данные, используя те же самые операторы INSEpt, UPDATE я DELETE, как и е постоянными таблицами, Они могут также быть индексированы, если необходимо. Фактически, времениая таблица — обычная таблица, которая имеет очень короткую продолжительность жизни. Обратите внимание также, что любой пользователь может создавать и использовать их, когда считает это нужным.
В более ранних версиях SQL Server временные таблицы часто использовались как обходной путь преодолеть некоторые ограничения. В SQL Server 2000 они используются гораздо реже. Использование временных таблиц в запросе будет всегда медленнее, чем непосредственное использование SQL. Например, предаоложим, что мы хотим попробовать определить число хранимых процедур и функций, которые имеют или два, или три параметра. Использование временной таблицы для этого могло бы оказаться идеальным решением: — создание тавдаяда для размещения имен объектов CREATE TABLE «counting ( specific_nane sysname ) — размещение всех строк, которые имеют от 2 до 3 параметров INSERT INTO (counting(epeci£ic_na«e) SELECT specific_naae FROM information_schema.parameters AS parameters GROUP a'i specific_name HAVING COUNT(*) BETWEEN 2 Mffi 3 - - подсчет числа значений а таблице SELECT COUNT(*> ' ' ' ' FROM (counting DROP TABLE tcounting Здесь создается локальная временная таблица emmirming (подсчет), содержащая один столбец speaiflc_natne (специальное имя) типа дузпаяе. Далее в нее помешаются имена хранимых процедур. у которых имеются 2 или 3 параметра, поем чего подсчитывается число записей. В заключение таблица удаляется. Этот код возвращает дужное число (которое, конечно, будет изменяться от сервера к серверу): 355 Немного подумав, мы мажем избежать нспольаования временной таблицы вообще и выпаивать только SQL-запрос, увеличив скорость запроса: I SELECT count (-) FROM (SELECT specif1с_пая» FROM informatio»_scbema.pa.retneters AS peraneters GROUP BY specific_name HAVING count l«J BETWEEN 2 AND 3) AS TwoFarms Мы исследуем amam пример подсчета параметров снова в следующем разделе, посвященном курсорам.
Временные таблицы можно Выло вы избежать более просто, если использовать следующие редко применяемые особенности T-SQL: Q Вторичные таблицы — одно из иаиболев важным дополнений к языку T-SQL — возможность включать подзапросы в операции присоединения. Это — чрезвычайно полезная техника и позволяет нам выполнять большинство действий, которые мы раньше делали вы с использованием временных таблиц в наших запросах, наподобие следующего: I SELECT tableName .виЬаиегуТаЫеКажв, alias.subqueryTableNanteld AS aliasKey FROM tableName JOIH (SKX2CT subqueryTableNacield, ЬаЫаМшмХМ TOOM subqueryTableHains} *S tUu OK tableName.tableKameld = alias.tableHameld Здесь в основном запросе выделяются столбцы subqueryTableName (имя таблицы подзапроса) из таблицы tableNaine (имя таблицы,} и subqueryTableNaaeld (идентификатор имени таблицы подзапроса), формируемый в подзапросе, в котором одновременно выделяется столбец tableNameM (идентификатор имени таблицы) и которые образуют вторичную таблицу alias (псевдоним). Подзапрос действует так же, как однопользовательское представление в запросе, и позволяет вам использовать операторы GROUP BY и having, при этом вам ие нужна временная таблица, чтобы выполнить запрос, который по этой причине будет намного быстрее, Обратите внимание, что, если необходимо, мы можем вкладывать вторичные таблицы в другие вложенные таблицы. □ UNION [АЩ — часто временная таблица будет строиться, используя два набора и помещая их вместе, Во многих случаях вместо этого можно использовать операцию UNION ALL. Выло время, когда я получил 40 процеиж® улучшения скорости, используя UNION ALL вместо временной таблицы. Аналогично, если для полного набора используется заключительный оператор WHERE, мы можем поместить объединенный набор во вторичную таблицу! □ Большов число таблиц в одном запросе — в некоторых случаях может быть’ целесообразно разделить длинные ванроеы на небольшие, более приемлемые части кода, разбивая их на запросы к временным таблицам. Одаако SQL Server 2000 позволит организовывать использование в одном запросе до 258 постоянных -таблиц, и это часто позволит выполнить запрос лучше, чем вы могли раньше. Это все выглядит как последний гвоздь в гроб временных таблиц. Однако они могут быть все еще полезны, когда мы должны некоторым образом управлять данными, когда требуется более одного прохода, Я использую временные таблицы весьма часто при реализации рекурсивного дерева для отображения на «кране — добавления значений для каждого уровня дерева следующего уровня. Временная таблица идеальна в этой конкретной ситуации, потому что мы должны повторять проход через набор, чтобы обратиться к каждому уровню дерева, и должны где-то хранить промежуточные значения. В следующем примере мы возьмем рекурсивную таблицу, которую назовем туре (тип). Каждый из типов может иметь неограниченное число подтипов. Мы должны упорядочить их и показать как дерево с подэлементами, непосредственно следующими за другими подтипами. Дерево данных имеет следующие узлы, с которыми мы будем иметь дело:
— сначала создаем «аблицу type CREATE TABLE type ( typeld int, -- не автоинкрементный, поскольку нам нужна сеются к — этому элементу в данном фрагменте parentTypeld int NULL, name varchar(60) KOT NULL ) — затем № жомы заполнить таблицу type INSERT INTO type (typeld, parentTypeld, name) VALUES (1, NULL, ’Вершина') INSERT INTO type (typeld, parentTypeld, name) VALUES (2, 1, 'Потомок 1 вериины') INSERT INTO type (typeld, parentTypeld, паи») VALUES (3, 1, 'Потомок 2 вершины') INSERT INTO type (typeld, parentTypeld, name) VALUES (4, 1, 'Потомок 1 потомка 1 вершины') INSERT INTO type (typeld, parentTypeld, name) VALUES (5, 2, 'Потомок 2 потомка 1 вершины') Теперь создадим запрос, используя временную таблицу, которая будет отображать дерево. Заметьте, что мы здесь используем временную таблицу в качестве массива для размещения некоторых величии, когда мы выполняем дополнительные проходы — оператор SELECT здесь будет недостаточен: CREATE TABLE tholdlterns ( typeld int NOT NULL, parentTypeld int tWLL, name varchar (IGO), IdentLeveX int, scrtOrder varchar(1000) — первичньй ключ таблицы table — рекурсивный атрибут — название типа — уровень в дереве для определения отображения — порядок сортировки для окончательного — отображения Здесь мы создаем таблицу fholditems (хранилище элементов) е пятью столбцами; typeld (идентификатор типа) типа int, который, не кат принимать значение NULL, parentTypeld (идентификатор типа предка) типа int, который может принимать значение NULL, пале (мвзвоиив типа) типа varchar (100), identlevel (уровень отображения) типа int. soxtOrder (порядок сортировки) типа varchar (1000).
Затем мы можем добавить вершину элементов дерева во временную таблицу: Далее мы должны закодировать цикл, чтобы работать с нижними уровнями таблицы по одному, организуя строки для выхода: a ECLARE ScurcLevel int ЕТ gcurrLevel - 2 WHILE 1 — х -- так как в Т-SQL нат цикла REPEAT .. UNTIL BEGIN INSERT INTO iholdltems — добавление порядка сортиров»! этого "лементс к текущему порядку сортировки, — учитывая порядок предка SELECT type.cypeld, type.parentTypeld, type.name, 0currLevel AS identbevel, RTRIM(tholdxtems.sortOrder) + LTRIM (replicate (’ 0', 10 — LEH (CAST (type, typeld AS char(10J))j + CAST(type.typeld AS char(lD))) FROM dbo.type AS type — это присоединение дает нам запись-потомок элемента — в таблице fholdltema JOIN fholdltems ON type.parentlypeld - fholdlteros.typeld — currLevel дает нам уровень, который мы добавляем. — так что нам нужен предок в JOIN WHERE Iholdlterns.identLevel - ScurrLevel - 1 BREAK — иначе просто увеличиваем лчелчик и продоякаем SET OcuEELeveX = ecurrLevel + 1 END
Теперь мы имеем все заданные величины, которые нужны для отображения дерева SELECT typeld, parentTypeld, CA5T(name AS varchar {15}) AS name, identLevel FROM fholdltems ORDER BY aartOxder, parentTypeld. — удаление таблицы явно не требуется, если она в хранимой процедуре — или если юл разрываем соединение между выполнениями DROP TABLE fholdlterns Все это дает следующий набор строк: tdentLevel typeld parentTypeld name 1 NULL Вершина 2 1 Потомок 1 вершины 4 2 Потомок 1 потомка 1 вершины 5 2 Потомок 2 потомка 1 вершины 3 1 Потомак 2 вершины 1 2 3 3 2 Bee это формирует основу для программы, которая имеет дело с двоичными (рекурсивными) деревьями, закодированными в SQL-таблицах. Расширяя этот алгоритм, мы можем делать почти все, что должны делать с двоичными деревьями, и при этом иметь хорошие результаты. Однако не забудьте: вели бы мы могли делать вто вез временных таблиц, то все было бы быстрее. Всегда перед использованием временной таблицы удостоверьтесь, что невозможно закодировать решение без нее. Курсоры В SQL обычно имеют дело с данными, представляющими одновременный набор. Мы видели это много раз в наших примерах. Однако не всегда возможно иметь дело с наборами данных. Курсоры — механизмы в Т-SQL. которые позволяют нам обратиться к отдельной строке и выполнить над ней действия, а не работать в предварительно выделенной группе строк. Имеются два различных типа курсоров, которые часто используются: □ Внешние API-курсоры ВД (API — Application Programming Interface — интерфейс прикладного программирования) — это специфические курсоры, которые используются, чтобы обеспечить внешние интерфейсы в SQL Server. Обычные примеры — ODBC, OLE DB и ADO. Используя курсоре, связанные с этими технологиями, программисты могут строить потоки данных для своих приложений. API-курсоры, прежде всею, используются прикладными программистами, и раз это так, они вне возможностей данной хши. Q Transact-SQL курсоры — эти курсоры используются в коде Т-SQL, п яы будем подробно рассматривать их в данном разделе. Они будут использоваться в цикле с SQL-швгом, чтобы выполнить операцию нал одной строкой в одно время.
Люди имеют противоположные мнения относительно мурсоров в коде Т-SQL. Здесь имеются две основные точки зрения: □ Курсоры — зло. □ Курсоры — очень полезные средства, которые мы должны использовать. Причины, почему курсоры выли названы “злом", двояки: О Во-первых, они приводят к неважным реализациям решения задач, которые лучше решаются на основе наборов SQL-операций. В атих случаях выполневие запросе, использующего курсоры, будет часто на порядок медленнее, чем при использовании операций с набором данных, □ Если мы не используем соответствующие типы курсоров, то завершаем размещением блокировок, для каждой строим нашей БД, к которой обря-цаемся. При иепользоваети T-SQL-курсоров проблема может выть менее существенна, так как наш T-SQL-код никогда не будет ожидать реакции-пользователя, ио проблема, конечно, все еще существует. Вторая точка зрения на использование курсоров также справедлива, так как имеются случаи, где вы должны их использовать. Однако с появлением определяемых пользователем функций имеется на самом деле только одна ситуация, где курсор обееиечиввет единственную возможность реализации, — когда вы должны выполнить некоторую команду с каждой строкой в наборе. Например, курсор позволил вы вам выполнить хранимую процедуру заказа, содержащую бианве-логику для каждой строки в таблице. Обсудим теперь основной синтаксис курсоров. Чтобы использовать курсор, вы должны взять команду SET: И SET 9<имя_куреора> “ CURSOR [FORHARD_ONLY I SCROLL! I STATIC I KEYSET I DYNAMIC ! FAST—FORWARD] |READJ3MLY I SCROLL—LOCKS t OPTIMISTIC] t«₽E_»ARNINGJ В FOR «оператор SELECT» I [FOR UPDATE [OF <столвец_1>, <стслвец_2>, ... , <столОе-_';> u Если вы использовали курсоры раньше, то, вероятно, Солее знакомы с синтаксисом DECLARE CURSOR. По моему млению, использование оператора SET, чтобы установить переменную курсора, является более последовательным. путем работы с ним. Это приводит к лучшему коду, так как все курсоры, воетрвенньм таким образом, — всегда лекальные курсоры и выглядят почти такими же, как обычные переменные SQL Senter. Поскольку они ограничены конкретным пакетом, их не нужно размещать а освобождать. Имя | SET 9<имя_курсора> - CURSOR Этот оператор объявляет переменную как величину типа cursor, Ее можно затем передавать всюду различным процедурам как параметр. Курсор обладает теми же самыми возможностями, как и обычная SQL-переменная, и это облегчает необходимость явным образом закрывать и перемещать курсоры.
Направление | [FORWARO_ONLY I SCROLL] Обычно вам удобно использовать курсор EORWARD_ONLY (только вперед), если только у нас нет каких-либо важных причин перемещаться в таблице назад и вперед (SCROLL — прокрутка). Курсор FOWfcRD_ONLY используют меньшее количество ресурсов, чем курсор SCROLL, во если вы должны перемещаться на предыдущие строки, он может не подойти. Мы обычно оставляем эту настройку незаполненной, поскольку настройка FAST-FORWARD из следующего набора необязательных параметров не будет работать ни с FORWARD—ОМ 1. У, ни со SCROLL, a FAST_FORWARD создает наиболее оптимизированный курсор в случае обычного использования. Тип курсора | [STATIC I KEYSET t DYHAMIC f FAST_FORWARD] С параметром STATIC (статический) SQL Server копирует нужные вам данные свою ВД tempdb, и затем вы можете переместить их назад. Никакие изменения, сделанные в основных данных, не будут видны в курсоре. Это значение используется по умолчанию, Курсоры KEYSET (набор ключей) просто делают копию ключей в te.-npdb и затем, когда вы запрашиваете строку, SQL Server выходит на физическую таблицу и получает и? нее данные. Вы ие можете видеть никаких новых данных, во будете ввдеть любые изменения в существующих данных, включая удаление. Курсоры DYNAMIC (динамический) несколько более дорогостоящие в смысле функционирования, поскольку значеюшг курсора не преобразуются в набор операций, пока вы реально не осуществляете выборку даиных с их помощью. С курсорамиkeyset элементы таблицы фиксируются на момент создания курсора. Курсоры же DYNAMIC получают свои элементы, определенные только тогда, когда курсор перемещен. Лучший выбор должен обычно определяться курсором FAST_FORWARD (быстрый вперед), но эта настройка устраняет любое не назначений направления и не позволяет выполнять прокрутку. Это не статически курсор, потому что вели вы выполняете любые изменения данных, они будут отображены. В одиом из примеров мы кратко покажем, как его свойство может влиять иа. курсоры, которые мы используем. Обновляемость I [READ_ONL¥ I SCROLLLOCKS I OPTIMISTIC] Параметр READ-ONLY (только чтение) должен быть очевиден, но другие два — не столь, очевидны. Оба они позволяют вам корректировать данные в курсоре. Одиаио ионользоваиие позиционных обновлений в кода Т-SQL не лучший вариант, поскольку это может действительно запутать, где вы точно находитесь в наборе данных. Если вы должны выполнять обновление, используйте оператор UPDATE с яеетатичесмим ТИПОМ курсора (KEYSET ИЛИ DYNAMIC): I UPDATE <и»Я—таОлидаА SET <поле> - value ' [ FROM <ии."таВлиин:> - WHERE CURRENT OF ®<иыя_пвремаиной-курсораА
Ключевое поле CURRENT OF сообщает оператору, что нужно изменить отроку, куда в настоящее время помещен курсор. Вы можете также выполнить и операцию удаления. Чтобы выполнить обновления в месте расположения курсора» мы можем использовать настройки SCROLL_LOCKS (щмиругка е блокировкой) и OPTIMISTIC (оптимистический). SCROLL_LOCKS гарантирует,-что наши обновления и удаления (выполяевиые в положении, определяемом выражением CURRENT OF) выполняются, помещая блокировки на основные таблицы. OPTIMISTIC использует столбец таблицы типа timestamp,, а вели он недоступен, строит контрольную сумму для каждой строки. Эта контрольная сумма используется точно •гак же, как оптимистическая блокировка, которую мы обсудили ранее, заново вычисляя перед изменением, чтобы вядагь, изменил ли другой пользователь строку. Однако, если вы ня находите действительно серьезных оснований для обновления данных с помощью курсора, не делайте этого. Намного более просто решить ту же самую задачу, используя код, подобный следующему: I UPDATE <имя_тавлицы> ЗЕТ <поле> - value FROM <;'ич _таб"иць:> WHERB <иервичн«Л_клвч> = <<пере»лнная_' «ьСорки' _иаподьвуищм_курсор> Он делает почти то же самое, что и курсор CURRENT OF, но более легок для чтения и отладки, так как мы можем распечатать переменную, когда выполняем наши примеры. Понижение типа курсора | [TYPE_KARNING) Если оператор SELECT, который вы опредаяига, не может использовать данный тип курсора, эта настройка (TypE^WARNIMG — щюцупреждение о типе) сэсбщекиг Л ошибке из SQL Server, предупреждая тЛ этом. Такая ситуация может возникнуть, если вы пробуете иоюжювать курсор keyset на наборе данных, ие имеющем уникального ключа, иа основе которого строится набор ключей, — SQL Server может использовать амосто этого курсор STATIC. Работа курсора Этот пример иллюстрирует некоторые проблемы использования курсоров. Мы хотим выяснить, какие хранимые процедуры и функции .в основной ВД имеют два или три параметра (как мы делали в примере с временной таблицей) на основе ^едетвмеиия PARAMETERS информационной схемы (1ЫГОВМАТ10Ы_5СНЕМА), Вудам рассчитывать время операций, как будто они выполняются на моем портативном компьютере Dell Pentium И 400 Мгц с 2&6 Мб ОЗУ. Сначала создадим временную таблицу, используя курсор: I SELECT getdate)) -- получение времени перед запуском кода — временная таблица лая размещения имен процедур во время никла CREATE TABLE tholdRxocedures ( specific_name sysnane ) DECLARE Scursor cursor, — переменная типа курсор Bc_routineb’aine syaname — переменная для хранения значений во время цикла SET Scursor - CURSOR FAST_FORHARO FOR SELECT DISTINCT specific_name FROM INFORMATION_SCHEMA,parameters
OPEN geursor — акаивиаация курсора . .= = FETCH NEXT FROM йсигзог INTO ec=routineNa«w -- получение первой строки WHILE 8„etch_status - 0 — означав?, что строка выбрана правильно BEGIN — проверка, находится ли число параметров данного конкретного — имени в пределах от 2 да 3 IF (SELECT count(•) FROM INFORMATION_SCHEMA.parameters AS parameters WHERE parameters.specific_name - gc^routineName) BETWEEN 2 AND 3 BEGIN — если это так, поыешение во временное хранилище - INSERT INTO IholdRpoceduree VALUES (8c_roi3tineHame) EHB - - - - FETCH NEXT FROM «cursor INTO Oc_routineName — получение «мумией строки ENO Здесь создается временная таблица fholdPpocedures (размещение процедур) с единственным столбцом speed£ic_neme (конкретное имя) типа sysneird а также две переменные Saursar (курсор) типа cursor и gc_roucine№aae (имя программы) ггипа sysneme. Далее инициируется курсор и в переменную gc_rmjtineNaae помещается имя первой процедуры. Затем ореамизуетея цикл просмотра всех имен процедур, й если очередная процедура имеет ат двух до трех параметров, ее имя помещается во временную таблицу, ... и если мы это сделали, то можем подсчитать число записей: I SELECT count(*j FROM IholdPrc-cedures SELECT getdateO — вывод времени после завершения :<ма Выполнив этот фрагмент, мы получим следующий результат 2001-01-10 23:46:59.477 355 2001-01-10 23:47:00.917 Это указывает, что тирания заняла примерно 1,5 секунды времеет выполнения. Это не Tta уж и медленно, но имеется, конечно, лучший nmwrft чмпмнения этой задачи, как показано газке: SELECT ge-da-e(j — снова для дамойетрацютлых целей SELECT count(*} — использование вложенного подзапроса для получения все* функций — с соотавтствуиимм числом параметров FROM iSELECT parameters.specifit_name FRON INKHSjATION_SCBEMfc, parameter» AS parameters - - GROUP B¥ paraiietere.specific name gAViHG count(*j BETWEEN 2 » 3J AS counts, | SELECT getdateO
В этот раз мы получим несколько лучший результат: 2001-01-10 23:50:25.180 355 2001-01-10 23:50:25.823 Теперь требуется 0.7 секунды, чтобы вьшолнн» вапрос, что составляет приблизительно половину времени, которое потребуется, чтобы решить ту ж» самую .чадачу, используя курсор. Кроме того, код гораздо короче и более легок для оптимизации в SQL Server. Хотелось вы надеяться, что этот пример хорошо демонстрирует, что курсоры являются большой проблемой при работе системы, используя намного больше ресурсов, чем требуют операции, основанные на наборе даиных. Использования курсоров Хотя я и предостерег бы против вход^его д.прявытеу испольгования курсоров, как я упомянул во введении к «ому разделу, они могут выть очень полезны в некоторых ситч ациях. Имеются обстоятельства, при которых, как я налагаю, курсоры будут очень лодеанвд □ Выполнение системных команд типа DBCC (Database Consistency Checker — модуль контроля непротиворечивости ВД) или системных хранимых процедур. □ Выполнение хранимых процедур для каждой строки в наборе данных. □ В случае, когда мы должны обращаться к каждой строке индивидуально, а не в наборе данных. Первые два из них довольно очевидны. Простой пример системной хранимой процедуры, когда нам нужно использовать курсоры, — процедура sp_adduaer (стандартная процедура добавления пользователя). Вели бы мы получали пользователей, например, из злектронной таблицы, мы, вероятно, импортировали бы данные во временную таблицу в SQL Server, пробежали Вы через все значения, используя курсор, и добавили новых пользователей в ВД. Второй случай отличается от первого, т не намного. Веда вы должны выполнить хранимую процедуру отдельно для каждой строки в наборе данных, вы обыиво должны использовать курсор. Эго —> довольно стандартная операция (типа реализации бизнес-правил е помощью хранимой процедуры), хотя и не оптимальный способ использовать такую ситуацию, если вы не владелец системы и вам нужно получить данные. Например, если мы не являемся владельцами системных таблиц, но должны добавить в них данные, то, вероятно. рейхи, что вызов хранимой процедуры в цикле — единственный способ выполнить зту задачу. Вели бы мы были владельцем этих таблиц, то могли бы выполнить эту задачу, используя вместо итого триггеры- Третья ситуация такова, что мы ножам получить неприятности. Имеется чрезвычайно ограниченное число причин, почему вам потребовалось бы просмотреть каждую строку в нашем наборе данных одну за другой, Единственная общая ситуация, где я использовал вы курсор, и где ие вызывал бы некоторый тип функции, демонстрируется в следующем примере. Здесь мы собираемся реализовать специальное упорядочивание набора данных. Загрузим набор данных со случайными значениями и затем единообразно перенумеруем набор, используя курсор. Пусть мы вмеем следующую таблицу:
— таблиц* для демонстрации курсоров CREATE TABLE item ( itemld int IDENTITY CONSTRAINT XPKIteffi PRIMARY KEY, sortOrderld int CONSTRAINT xAKXten sortOrder ORIQ'JE ) Зйееь еозйаетм таблица item (элемент), содержащая два столбца: автаыкреаанпишй столбец itemld (идентификатораммвнта) типа int и. sortOrderld(идентификатор порядка сортировки). Оба слулгбда являются ключами и представляют соответствующие ограничения. Первый шаг — загрузить таблицу, ааполняя порядок сортировки случайными числами; DECLARE gholdSortOrderld int, gcounter int SET Scounter - 1 -- иниииамаация счетчика WHILE gcounter <« 5 begin . : - --- используем случайные числа между 0 к 100 SET ghaldSdrtOrderld - CAST(RAND (I * 10G AS ir.Ii — проверка, что случайное число не судаствует уже : — как порядок сортировки IF КОТ EXISTS (SELECT - FROM dbo.itee, WHERE sortOrdexId - BholdSortOxderldl BEGIM — поместить числе INSERT INTO dbo.item(sortOrderld) VALUES (eholdSoxtOtderldl — увеличение счетчика SET geeunteT - всоипШЕ + 1 ENO END Как только мы загрузим нашу таблицу, посмотрим результат: I SELECT * FROM dbo.icem ORDER BY sartOrderld Это дает нам достаточно случайным набор чисел, которые мы захотим реорганизовать на еледуюадем шаге: itemld sortOrderld 4 29 2 35 1 71 3 72 5 81 ЧОО
Теперь мы создадим курсор для перемещения по всем элементам в нашей таблице, упорядоченной, по значениям порвдка сортировки, и будем обновлять каждую строку отдельно: DECLARE gcurSOr CURSOR, — переменная типа CURSOR ®c_itemld int, ic_sortOrderId int, — переменные элементов курсора gcurrentSortOrdeEld. int — используемся лля увеличения — порядка ссрторсехи SET flcurrentSortOrderld ~ 1 — инициализация счетчика — используем курсор STATIC, так как j® просматриваем измененные величины SET leuraor - CURSOR FPRWASD_a№ STATIC FOR SELECT iternld, wrtOrderld FROM dbo.item ORDER BY aartOreferld OPEN всигзог — активизация курсора — получение первой строки FETCH NEXT FROM gcursor INTO ®c_itemld, ec_sortOrderxd — fetch_status « 0 говорит, что выборка точная WHILE 8IFETCH_STATUS • 0 — это означает, что строка была выбрана аккуратно BEGIN — обновление таблицы помещением новой величины порядка сортировки UPDATE dbo. item. SET sortOrderld - ecurrentSortOrdBrld WHERE itemld - ec_lteeld — увеличение счетчика порядка сортировки, чтобы добавить пустой — промежуток, равный 100 - SET ScurrentSortOrderld = ecurrentSortOrderld + 100 -- получение спегуксей строки FETCH NEXT ЕЖИ gcursar INTO ec=itemld, gcsortQrderId END Так как мы добавили строку, чтобы вывести ключи каждой строки, таблицы, мы получи следующий выход: ©cjtemld @c_sortOrder!d @curren-SortOrderld 4 28 1 ©cjtemld @c_sortOrder!d ©ourrentSortOrderld 2 35 101 @cjtemld @c_sortOrderld ©currentSortOrderlcI 1 71 201 ©cjtemld ©c_sortOrderid ©currentSortOrderid 3 72 301
©cjtemid @c_sortOrdedd ©currentSortOrderld 81 401 5 Теперь посмотрим зтачвняя в таблице: SELECT * FROM dbo.item ORDER BY SOrtOrtertd Если все выполнено правильно, строки выхода будут в том же порядке: itemld sortOrdertd 4 2 1 3 5 1 101 201 301 401 Но что, если мы изменим тип курсора иа DYNAMIC, подобно курсору _FAST_FCRWARD? Если мы заменим часть объявления курсора FCRWARD_ONLY STATIC на £AST_FORBARD, то получим результаты наподобие следующего: @cjtemld @c_sortOrdertd ©currentSortOrderid 4 29 1 @cjtemld @c_sortOrdedd @currentSortOrdeffd 2 35 101 @cJtemW @c_sortOrderld @currentSortOrderid 1 71 201 ©cjtemid @G_sortOrdertd @currentSortOrdedd 3 72 301 ©cjtemid @c_sortOrderid @currentSortOrdedd 5 81 401 ©cjtemfd @c_sortOrderld @currentSonOrderld 2 101 SOI ©cjtemfcj ©c^sortOrderld ©currentSortOrderld 1 201 601
@cjtemld @c_sortOrderld ©currentSo-tOrderld 3 301 701 ©cjtemld ©c_sortOrdarld ©currentSortOrdend 5 401 801 ©cjtemld ©cjsonOr derid ©currentSortOrderid 2 501 901 ... и так далее. Причина этого в том, что значения в месте расположения курсора изменяются и завершаются переупорядочиваиием в «ответствии с порядком сортировки. Так как порядок сортировки изменяется более чем до следующей величины, вы никогда не достигнете конца последовательности. Всегда определяйте тип курсора, который вы хотите использовать, иначе вы можете получать непредсказуемые результаты. Обработка значений NULL В главе 10 мы обсуждали поля, допускающие значения NULL. В атом разделе мы должны пролить свет на программирование с использованием, значений NULL и особенно, как сравнения с NULL обрабатываются, в SQL Server, и что это означает в вашем коде. Со значениями NULL нужно довольно хитро работать, но их трудно избежать в ваших ВД. Они могут оказаться довольно аффектными операторами. Можем ли мы задать каждому полю в ВД условие NOT NULL? Да, но каждый раа, когда мы используем в операторе внешнее соединение, все невоавращаемые значения будут заполнены значениями NULL. Сейчас мы рассмотрим несколько другой подход к значениям NULL, которые используются в логическом выражении, и я обязан доктору Давиду Розенштейну за разъяснение мне этой концепции. Рассмотрим некоторые из наиболее интересных проблем, которые возникают, когда мы сталкиваемся с данными NULL, и которые обычно игнорируются. Что означает NULL? Согласно SQL Server 2000 Boots Oniine "Значение NULL указывает неизвестную величину. Значение NULL отличается от пустого или нулевого значения, Никакие два значения NULL не равны. Сравнения между двумя значениями NULL или между NULL и любым другим значением возвращают неизвестный результат, потому что значение каждого NULL неизвестно". Итак, довольно ясно, что NULL означает неизвестное значение. И это понятие помогает нам в случае, когда поле может иметь значение NULL, и даже в случае объединения, когда соответствие одного и другого набора даиных неизвестно. Однако при программировании с использованием логики е тремя значениями (истинный, ложный и NULL) мы должны быть немного хитрее, чем в этом случае. Проблему довольно просто продемонстрировать. Рассмотрим следующий оператор:
Ц SELECT CASE WHEN NOT (1 - MOLL} THEM 'True' ELSE 'False' END Очевидно, что £1 => 1) дает Tree, a (1 = 01 дает False, А как отяоеительяо (1 ~ NULL}? Я полагаю, что False — логичный ответ. И очевидно, что НОТ - (Falsa) - True. Однако выполнение этого запроса возвращает другое значение: False Прекрасно, но если NOT (1 “ НШЛ>) равно False, тогда NOT (NOT (1 - NOLL J) должно быть True, не так ли? Итак, мы выполняем следующий оператор: g SELECT CASE WHEN NOT- (NOT (1 -NULL}) THEN 'True' ELSE ' False’ Е».: И он снова возвращает False! Это фундаментальное свойство трехзначной логики — ежа из наименее понятых принципов в программировании SQL Server. Проблема связана с определением NULL как неизвестной величины (Unknown). Логически говоря, что является противоположностью неизвестной величине? Известная величина (Known). Итак, заменим (1 - NULL) на Unknown, потому что логически, если NULL — Unknown, то Unknown является и равенство Unknown и единицы. Поэтому NOT (1 - NULL) должно быть NOT (Unknown). И это равняется Known, правильно? Возможно, в английском языке (и в русском — Прим, перво,}, но ие в трехзначной логике. Возможно Что, если мы используем 'Возможно" (Maybe) вместо неизвестной величины ( Unknown)? Итак, (1»NULLJ —Maybe. А противоположность Maybe? .Снова Maybe. Рассмотрим следующие таблицы истинности: AND True False OR True False True True False True True True False False False False True False Рассмотрев несколько примеров для агой таблицы, мы видим, что True AND True - True и True AND False = False. Далее мы рассмотрим стандартные таблицы истинности, включая NULL. Эти таблицы взяты из SQL Server 2000 Books Online, но значение Unknown заменено на Maybe: AND True Fahe Null OR True False Null True True False Maybe True True True True False False False Maybe False True False Maybe Null Maybe Maybe Maybe Null False Maybe- Maybe шее
Итак, мы видим, что True AND NULL - Maybe, True OR NULL - True. Другое интересное сравнение — (SULL = NULL). Явно, это выглядит как True, но как могут бия» равны две неизвестных величины? Неионягяо-, и результат — Maybe. Далее приведены таблицы истинности NOT AND и NOT OR: NOT AND True False Null NOT OR True False Nidi True Falsa True Maybe True False False False False True True Maybe False False True Maybe Null Maybe Maybe Maybe Null Fake Maybe Maybe Важно проверить эти две последние таблицы, используя (1 = 1)— True, £1 = 0) — False и (1 « NULL) — Maybe, Например: SELECT CASE ИНЕН (NOT (fl - 1) OR (1 - NULL)) "AND (1 - -1} ) THEN ELSE END 'True' * False' Мы возьмем каждое логическое выражение и оценим его: {NOT (True OR Maybe) AND True) - NOT (True AND True) - NOT(True) = False ... получив следующее значение, выполняя оператор: False Основной ключ в понимании некоторых уродливых логических ошибок в программировании SQL Server связан е фантом, что SQL Server оценивает оператор, и а пользуя соответствующую трехзначную логику, но в заключительном анализе обращается со значением NULL как False, Однако тая как большое число наиболее важных запросов, которые вы будете писать, включает много критериев поиска, важно понять, что значение NULL сохраняется как ЖИЛ до самого конца. Рассмотрим оператор WHERE для данной строки, пытаясь понять, почему он не возвращает значение, которое мы ожидаем: В WHERE NOT ({fieldl - Sfieldl) OR (field? - ifield2)) В следующей строке мы заменим все значения полей и переменных в операторе данными, которые мы сравниваем: | WHERE NOT 111 - 1) OR (NULL - NULL)) Итак, мы завершаем следующшет равенствами: NOT (True OR Maybe) = NOT (True) = False. Это кажется довольно разумным. Однако давайте слегка изменим значения: I WHERE НОТ ((2-1) OR (KULL - NULL))
Теперь мы имеем NOT (False OR Maybe) - NOT (Maybel - Maybe. Обратите внимание, что кагда SQL Server заканчивает сравнение со значением Maybe, в завершение он преобразует его в False. Возможно, более безопасно работать таким пессимистическим способном, так как возможно, что эта величина действительно является ложной. В то время как ANSI-стандарт, для сравнения ILL'LL = NULL) дает NOLL, SQL Server 6J> и более ранние версии считали, что (NULL = NULL) - True, В SQL Server 2000 кт настройка (SET ANSI_NULLS — установка ANSI-NULL), которая позволяет, переключиться на (NULL- - NULL) - True, Однако использование зтой настройки не рекомендуется, если у вас нет проблем совместимости с ранее написанными программами. Она может причинить большее количество неприятнаствй, чем дает выгод, и зта настройка, вероятна, в дальнейшем не будет существовать. Проблемы со сравнением данных, которые включают значения NULL, являются девш&но общими, и такие ситуации можно использовать, но с чрезвычайной осторожностью, если там есть значения NULL. Мы должны быть уверены, что повяли следующее: □ (NULL - 3) имеет значение Maybe; □ (NULL > 3) также имеет значение Maybe. И пример, где это будет вас шокировать: NOT (fieldl “ 3), где fieldl — NULL, дает NOT (Maybe) = Maybe и SQL Server окончательно оценит это как False. IS NULL Единственное сравнение, которое работает в любом случае со значениями NULL — это оператор IS NULL (является NULL>. Мы можем определить, какие значения являются значениями NULL, используя: Ц WHERE fieldl IS NULL Этот оператор возвратит True, если поле — NULL, и False — в противном случае. Имеется также оператор IS NOT NULL, являющийся логической противоположностью оператора IS NULL. Используя эти два оператора, мы можем формулировать соответствующие сравнения с двумя значениями NULL. Например, если мы хотим определить, являются ли два значения, которые могут принимать аначетияNULL, "рапными", означающее, что они имеют одно и то же значение или оба равны NULL, вам потребовались бы три части сравнения: I WHERE fieldl - efieldl OR (fieldl IS NULL AND fieldl IS NULL) Это может казаться довольно очевидным, но из всех ошибок логического кодирования, которые я наше* или сделал, наиболее распространенные те, которые включают преобразования в полем, которое требует аиачеияй NULL,
Предположим, что мы имеем представление, ив которого-я строю преобразование, а запись-потомок одаоЛ из таблиц, дающей адрес человека, недоступна. Однако не вее люди имеют щарес офиса, поэтому мы должны использовать внешнее сое ди некие. CREATE VIEW v_perSOO AS SELECT person,personid, person.firstMame, person.lastNanw, address,screetHame FROM dbo.person AS person ' : LEFT OUTER JOIN dbo.personAddress № personAaaress S dbo. address AS address ON address.addrassld - personAddreas.addregsld AND personAddress.type «1 —; адрес офиса OH person.personid “ personAddress,personid Здесь v_person — представление “человек"; person — таблица "человек”; personid — идентификатор человека; firstName — имя; lastName — фамилия; address — таблица "адрес"; screetttame — название улицы; personAddress — адрес человека; addressld — идентификатор адреса; type — тип (адреса). Теперь создадим преобразованный запрос, чтобы вставить любые строки, где person Id в таблице не существует. Это довольно легко сделать. Затем ин закодируем следующий оператор UPDATE, чтови обновить значения для человека, если что-либо изменится: UPDATE person SET person.firstKame - v_perscn. firstNarae, person.lastName v_person.lastNaaie, person.streetName - v_person.streetName FROM dbo.person AS person JOIN dbo.v_person AS v_j>erson ON person.personid ~ v_psrson.personid where person.lastName <> v_persc-n.lastNanie OR person.firscName о v_persen.firstName OR person.streetKaise <> v_person.streetNa»e - Это выглядит в значительной степени правильным, но все же имеется зияющая брешь. Даже считая, что все поля в наших основных -таблицах не допускают значений NULL, в нашем представлении существует левое внешнее соединение, при котором st гее tName может быть NULL. Если запись personAddress не существует, могут возникнуть обстоятельства, когда v_person. streetName будет иметь значение NULL, но если person. streetName не NULL, эта величина все же оценивается как False, Подставив реальные данные в следующий оператор: WHERE person,lastNaine v_person. lastHame Ц OR persen.streatNaee <> v_person.streecNa»e ... мы получим: WHERE 'Davidson' <> 'Davidson' OR ’Worthdale’ <> NULL Возвратившись к таблицам истинности, получим, что (False OR Maybe) = Maybe. И SQL Server оценивает это как False! Это очень легко сделать, так что вы должны написать запрос следующим образом:
I WHERE person.lastMame о v_person.laatNaae OR person. EirstNaiw <> ч__pereon. f ixatHwae - - - OR (person.streetHame <> v_₽erson,st««etWase . . OR (person.streetNarae IS KULL АЯВ v_person.streeбМаяв IS NOT HJbLJ OR (person,streatKame IS NOT NULL AND IS КОЬЬН Этот код мучителен для написания. но это единственный нуль, как мы можем аффективно сравнивать с NULL. NULL в других операциях Последнее замечание, которое следует сделать относительно значений NULL, касается использования их в других операциях. Когда мы имеем дел© с величиной NULL не в логическом сравнении, а в других ситуациях, лучше считать, что это "Неизвестно”. NULL не означает, и ие должен означать “ничего”. NULL означает что-то, с чем нужно также обращаться, хотя его идентичность неизвестна, Например: □ Числовые вычисления: NULL + 10 означает "Неизвестно", так как если мы не знаем, с чего начинаем, а затем добавляем 10. ничего не изменяется. □ Объединение строк: ' *' + NULL + ' также означает "Неиззестно", так как.нет никакого способа узнать, какая строка фактически получилась. □ NULL + NULL: очевидно, что два "Неизвестно" те могут дать ничего другого, кроме "Неизвеетио". Следовательно, когда мы имеем дело с операцией, когда один или несколько параметров может быть NULL, вы должны использовать одан из нескольких механизмов, которые обеспечивает SQL Server — ключевое слово САЗЕ (вариант) и функций ISttULL (равно‘NULL) и COALESCE (объединение). Например, чтобы выполнить следующее объединение: Ц SELECT + «field + где gfield может выть NULL, я мм хотим получить результат, не равный NULL, если Ifield имеет значение NULL, то можем ввить следующие три части: I SELECT CASE вfield WHEN NULL ТИЕ8 " ELSE »field END или Ц SELECT ISNULL («field, ") ИЛИ В SELECT COALESCE (gfield, "J ' 1 * * * * * * В ' ' - Каждая из них имеет свои достоияства: □ CASE — использование ключевого слова CASE здесь симпатично, потому что оно сам© все объясняет. Это — очень мощное средство, которое можно использовать не только для простой проверки1’11.
□ ISNULL имеет два параметра. Она проверяет, является ли первый параметр значением NULL, и затем возвращает второй параметр (в приведенном примере — пустая строка), если зто так. Вели второй параметр —'NULL, NULL и возвращается. □ COALESCE позволяет вам задать насколько параметров, и функция выбирает первое значение, не равное NULL. Например, COALESCE (gfield, ^variable, IvariableZ + ' *', ") сначала будет проверять if ield, затем evariable и так далее. Это эквивалентно оператору CASE, который дает: SELECT CASE WHEN «field IS NOT NULL THE» Sfield WHEN «variable IS NOT NULL TUBS «variable WHEN evariablez +*'*' IS MOT HULL THEN «variable? + Хотелось бы надеяться, что этот раздел прояснил нжоторые кз проблем, которые нужно иметь в виду при программировании с возможностью появления значений HULL. Они могут быть достаточно неприятны при реализации, но нет никакого способа избежать их использования. Представления Как мы обсуждали в главе 3, о предетмлевии можно думать как о виртуальных таблицах, и они обладают большинством характеристик обычной таблицы ВД. О точки зрения наших SQL-операторов они — таблицы, хотя обычно и ие поддерживают операторы корректировки (за исключением очень специфических случаев). На самом деле мы, как правило, и не хотим использовать корректировку представлений, так как они предназначены для упрощения Рассмотрения нами данных. Обратите внимание, что представления не являются компилируемыми объектами подобно хранимым процедурам, а, скорее, реализованы как текст, который может быгпв вставлен в запрос, где ото необходимо, точно так же как вы вставляете полученный запрос в оператор. Имеется несколько общих случаев, когда представления будут использоваться нашей ВД: □ Для маскирования деталей реализации — они могут использоваться, чтобы скрыть от пользователя большое количество соединений. □ Как средство формирования отчетов в реальном масштабе времени — они могут использоваться, чтобы создать отчеты, которые связаны с нашими текущими данными и не затрагивают само функционирование, определяя набор конкретных запросов, которые часто нужны пользователям. □ Как механизмы безопасности — давая доступ к представлению, а не к основным таблицам, мы можем реализовать безопасность на основе строк или столбцов. Далее рассмотрим эти ситуации. Представления как устройства маскирования Представления — превосходные средства для маскирования даиных. Мы можем взять наши высоко нормализованные таблицы, соединить их с другими таблицами в нашей ВД и дать нашим пользователям данные, как им желательно, скрывая от них основную ВД и сложности фактического механизма, с помощью которого мы обращались к данным. Представления могут также скрыть все нежелательные данные, позволяя пользователю сосредоточиться на том, что является действительно важным.
Предположим, что мы имеем следующие две таблицы в воммерчесжой ВД. Таблица item (изделие) представляет продаваемые изделия, а таблица sale (продажа) определяет число проданных изделий: — изделия, которие ив предаем CREATE ТАВ» item ( itemld int not null identity, name varchar{60} NOT NULL, price money — записи продаж CREATE TABLE sale saield int HOT NULL IDENTITY itemld int HOT NULL, date datetiae KOT NULL, itemCount ir.t — внешний ключ к таблице icero числа проданных изделий Здесь создаются две таблицы леем (изделие-) и -sale (продажа). Первая таблица содержит три столбца: автоинкрементный itemld (идентификатор изделия} типа int; пале (название изделия} типа varchar (60} и price (стоимость) типа money. Вторая таблица содержит четыре столбца: автоинкрементный salela (идентификатор продажи) типа int; itenld (идентификатор изделия) типа inc; data (дата) типа datetime и dtemCount (число изделий) типа int. Мы могли Вы захотеть создать представление, чтобы разместить информацию об изделиях и продажах в одной таблице для просмотра пользователем. I CREATE VIEW v_aale AS SELECT sale.saleld, sale.itemld, sale.date, sale.itemCount, item,name, item.prtee FROM dbo,sale AS Sale JOIN dbo.item AS item ON sale.itemld - item.item.Id Теперь, всякий раз, когда пользователю нужно посмотреть на продажу, представление будет оберегать его от необходимости вручную присоединять ее к таблице item. Это — довольно простой пример, но когда десять или большее количество- таблиц включены в представление, сбережения времени при кодировании могут быть довольно большие. Плюс, еишмаияяются столбцы таблиц, например, когда мм делаем дальнейшую нормализацию и вместо хранения стоимости в таблице item храним ссылку на отдельную таблицу стоимостей, где размешается само значение, пользователь представления не должен дая® и знать об этом. Представления в отчетах, выполняемых в реальном масштабе времени Формирование отчетов в реальном масштабе времени — термин, который указывает, что отчет не может иметь никакого времени ожидания, то есть его данные непрерывно обновляются. Если мы хотим иметь немедленный, сиюсекундный отчет, одним из способов реализовать это является использование представления. Представление дает нам пару выгод по сравнению с написанием запроса непосредственно дли таблиц, как вы обычно пишете код для отчета:
Q . Представления предотвращают иереглиментироеанные запросы отчета — нерегламентировагтые запросы к реальным-таблицам могут привести к большим всплескам в загрузке сервера. Создавая представление или представления, мы можем попробовать передать по каналу любые запрашиваемые потребности в реальном масштабе времени, использующие набор запросов, которые были оптимизированы, проверены, и имеют меньший шанс воздействия на общее функционирование системы. □ Представления позволяют избежать блокировок — обычно мы можем создавать отчеты таким образом, что они будут игнорировать блокировка БД и, что более важно, также ив оставлять блокировок в БД. Это достигается, используя указание оптимизатору reaSuncommitted (или nolock — бе» блокировок) — см. выше. Такое указание должно использоваться только тогда, когда оцененные данные приемлемы, так как некоторые из читаемых записей могут выть в транзакции, которая может откатиться. Этот путь, независимо от того, как пользователь пробует работать с представлением, не будет блокировать никаких ресурсов. Возвращаясь к нашим таблицам, пусть пользователю нужно видеть сиюминутные почасовые цифры продаж. Мы выясняем у пользователя, что можем игнорировать те случаи, когда продажа зарегистрирована, но может выть позже удалена. Таким образом, мы могли бы создать следующее представление для пользователя, чтобы строить с ним запросы: CREATE VIEW v_saleHourlyReport AS SELECT item.itewld, dbo.dateSrereveTi«e(sale.date) AS saleData, dateRart(hour, -sale.date) AS saleHour, sun:(sale.itemCount) AS totalSold, sum (sale.ItemCount * item.price) AS soldValue IROM dbo.item AS itemtreaauncommited.) JOIN dbo.sale AS eele (re-aduncwmitedl OH sale.itenild — item.iEejald •— группировка информации no itwnld, вазам no дням и часам дня — чтобы получить то, что запрошено GROUP BY item, iteasld, item.пате, dbo. dateSremoveTima(sale.date), datePart(hour, sale.date) Обратите внимание, что здесь мы используем системную функцию datePait для выделения часов из даты, и функцию из главы 11, чтобы отделить время от даты. Теперь этот запрос может быть выполяеа неоднократно с меньшим воздействием на продолжающиеся процессы, так как мы удалили возможности блокировки чтения хранящихся данных, когда они используются (с операторами readimcoamitted). Конечно, ати меры настолько хороши, насколько хороши средства, которые созданы для их использования. Даже без блокировки возможно использовать обширные системные ресурсы, если представление соединяется с другими представлениями неблагоприятным образом. Представления как механизмы безопасности Имеются два свойства представлений, которые мы можем использовать, чтобы строить более безопасную БД, Первое — назначение таких прав доступа пользователю, когда он может использовать представление, но не основные таблицы. Например, пусть у нас есть таблица person (человек):
CHEATS TABLE person ( perscnld int identity, firstSame varchar(2G), lastNaee varchar140} Здесь создается таблица регзоп:(человек)г-содержащая три столбца; автоинкрементный personld (идентификатор человека) типа int; firstName (имя) типа varchar(20) ; lastName (фамилия) типа varchar (40). Мы сможем сформировать представление на основе агой таблицы; I CREATE VIEW v_p«raOn AS SELECT personld, firstNaae, LastMaae FROM dbo.peraon Выбор данных из таблицы или представления возвращает одни и и же значения. Однако они представляют собой две разные структуры, так что мы можем отдельно назначать права доступа и ним. Второе свойство представлений, которое полезно как механизм Везоваеяоети — способность делить структуру таблицы, ограничивая строки или столбцы, видимые пользователю. Предположим, что мы имеем следующую структуру таблицы: Столбец 1 Столбец 2 -* •» < Столбец N Строка 1 Зарплата 1-го секретаря Номер социального страхования 1-го секретаря Адрес 1 - го l секретаря Строка 2 Зарплата 1-го менеджера Номер социального страхования 1-го менеджера Адрес 1-го менеджера Строка N Зарплата главного администратора Номер социального страхования главного администратора Адрес главного администратора Представления позволяют нам разрезать таблицу на секции, которые включают все столб цы, но не все строки на основе некоторых критериев. Это — безопасность на уровне строк, или горизонтальное разделение наших данных, В нашем примере мы могли вы позволить 1-му секретарю просматривать только строку 1, в то время как остальная часть строк будет -, ±:.. казаться, что не существует: Столбец 1 Столбец 2 , . Столбец N Строка 1 Зарплата 1-го секретаря Номер социального страхования 1-го секретаря Адрес 1-то секретаря
Следовательно, давая доступ к представлению только в виде этой строки, любой пользователь, кто имеет право доступа к представлению, будет иметь право видеть только ату конкретную строку. Возвращаясь к нашему примеру кода, мы возьмем предыдущее созданное нами представление и используем его как основу более конкретного представления, которое будет интересно только менеджеру изделия е itemld, равным 1. Это позволит менеджеру контролировать уровень предок данного изделия в течение дня: I CREATE VIEW v_saleHaurlyH.eport_sccksOn.ly AS select itemld, saleDate, saleHour, totalSold, soldValue FROM dbc.v_saleHourlyRepart WHERE itemld - 1 Мы обычно реализуем более широкий план безопасности, чем жесткое кодирование i cemld в представлении, но ото дает простой пример безопасности на уровне строки. Такой план мы рассмотрим позже в этой главе. С другой стороны мы можем использовать безопасность ва уровне столбцов. Неудивительно, что это также называется вертикальным разделением данных, поскольку мы будем разделять столбцы представления. В таблице item (изделие) имеется три столбца: itemld (идентификатор изделия), пате (название).и price (цена). Выло бы неправильно.не разрешать каждому пользователю ВД видеть все изделия с их названиями, но цена — это другое дело. Следовательно, мы могли бы сформировать представление следующим образом: i CREATE VIEW v_itemNo₽rice AS SELECT itemld, name FROM dbo.item Конечно, нет никакой причины, почему мы не могли бы одновременно делить наши таблицы горизонтально и вертикально, давая пользователю столько данных, сколько необходимо: Столбец 1 Строка 1 Как только мы построили необходимые представления, мы не разрешаем доступ к основной таблице, ио дадим пользователям^- кто -должен видеть данные, вместо второ доступ к предетавяеяио!. Волее полное обсуждение безопасности включено в дхледнюю часть агой главы. Что нового в представлениях? Имеются две очень интересные новые особенности представлений в SQL Server 2000. Эти особенности: □ индексирование представлений; □ разделение представлений, Ове эти особенности будут детально исследованы в следующих разделах. 47а
Индексированные представления Индексация представлений — очень интересная особенность, но доступна только в версии Enterprise (и версиях Developer и Evaluation) SQL Server 2000. Индексируя представление, мы теперь имеем возможность получать бея триггеров итоговые данные, которые автоматически поддерживаются средой SQL Server таким образ ом, который делает обычно сложное действие столь же простым, как написание запроса, Вместо определения представления, используемого как обычное представление, оптимизатор может выбрать использование индекса, который содержит текущие значения предварительно созданного индекса. Итак, вместо создания триггеров, которые суммируют наши данные в других таблицах, мы могли бы создать представление. Например, возвращаясь кяаШим таблицам item (изделие) и sale (продажа), мы могли бы еоадать следующее представление. Обратите внимание, что только схемно-связанные представления могут быть индексированы. Это дает уверенность, что таблицы и структуры, иа основе Которых создан индекс, не будут изменять полученное представление. I CREATE VIEW ._itefflSa1c ’ ” ’’ WITH SCHEBABIBDING : : AS -- SELECT item.itemld, item.name, suiafsale, itemCountf AS soldCount, sum(sale.itemCount * itern.ре1r-1 AS eerldValue ’ -- : FROM abo.itee JOIN too.sale ON item,itemld » sale.itemld GROUP BY item.itemld, item.name Вычисления будут производиться во время выполнения. Если оно недоста-rowo. быстро, или если использует слишком много ресурсов, мы могли бы добавить индекс к представлению, наподобие следующего: I CREATE INDEX XV_itemSale_mater£ali2e ON v_itemSale (itemld, aarne#. soldCount, scldValueJ SQL Server тогда построил бы представление и разместил индекс. Теперь наши запросы к представлению будут очень быетрва. Одаако хотя мы и избежали всех проблем кодирования, связанных с хранением итоговых данных, мы должны держать наши данные в актуальном состоянии. Веяний раз, когда в основных таблицах изменяются данные, индекс представления Судет изменять свои данные, так что имеется увеличение продолжительности раваты из-за поддержания индекса представления. Следовательно, индексирование представления означает, что ускоряется производительность для чтения, но яе обязательно дин обновления. . Имеются также и некоторые другие предостережения. Ограничения того, что может : использоваться в представлении, до этого проиндексированном, являются довольно тяжелыми. Наиболее важные вещи, которые вам не следует делать: □ Использование синтаксиса SELECT * — столбцы должны быть заданы явно. □ Использование UNION в представлении. □ Использование любых подзапросов. О Использование любых внешних соединении или рекурсивных соединений е той же самой таблицей.
□ Использование ключевого слова ТОР. □ Использование DISTINCT. □ Включение функции sum () , если она ссылаете я более чем на одни столбец. О Применение почти левой агрегатной функции к выражениям, которые могут иринимать значение NULL. О Ссылка на любые другие представления. □ Ссылка на любые недетерминированные функции. И это — не все! Имеется несколько страниц требований, которые должны выть выполнены, в SQL Server Books Online, раздел Creating an Indexed View Section (Создание секций индексированных представлении), но эти — наиболее существенные, которые должны выть рассмотрены перед иепольаовамием индексированных представлений. Хотя все ото может показаться довольно строгим, имеются серьезные основания для каждого из этих правил. Для нас обслуживание индексированного представления аналогично написанию собственной фуинции обслуживания денормализованных данных. Чем более сложный запрос должен бить сформирован для демормализовднных данных, тем больше сложности в поддержании его, как я считаю; во, вероятно, эти правила будут все более легкими в новых версиях SQL Server. Распределенные секционированные представления Распределенное секционированное представление в основном подобно любому другому представлению, в котором использованы данные из нескольких таблиц. При этом данные таблицы могут существовать на различных SQL Server. Этот процесс известен как интеграция (federation). Здесь мы будем иметь дело с группой серверов, которые управляются независимо, но которые разделяют бремя обработки в системе. Это может дать еущественшле усовершенствования работы, увеличивая параллельность ввода/вывода системы, Так как мы прежде всего интересуемся аспектами проектирования такого представления, то рассмотрим шаги создания структуры; □ Возьмем таблицу и создадим несколько точных копии ее. □ Разделим данные по некоторому значению (месяц, тип и т. д.) для. этих таблиц, и применим ограничени.е-проверку для осуществления разделения, □ Наконец, определим представление, которое использует операцию ON ЮМ ALL (без удаления двойных значений и каких-либо критериев сортировки), чтобы объединить копии таблицы в единственный набор результатов. Например, возьмем вашу таблицу sale (продажа). Пусть мы имеем только два изделия — ItemA с itemld - 1, и ItemBe itemld - 2, Мы могли бы тогда определить следующие таблицы: CREATE TABLE sale_itemA ( Baleld Lftt KOT NOLL, itemld int NOT HULL CONSTRAINT chkSsale^itemASitemldSOne CHECK itensCount ir.t NOT NULL ) CREATE TABLE sale_itet«B (itemld =i>,
saleld int SOT NULL, itemld int ЫОТ NULL COSSTRAIST chkSBale_ites®SitemIdSTw CHECK (itemld - 21, itemcount int hot null Далее мы создадим предетазление, которое ссылается на эти две новы» таблицы: create утей vaaie AS В SELECT saleld, itemld, itemCount FROM dbo.saie_itemA UNION ALL — Вез сортировки и удаления дубликатов S SELECT saleld, itemld, itemCdunt FROM dbo. sale_tt«te- SQL Server достаточно силен, чтобы использовать лишь -требуемую таблицу, и при этом может автоматически назначить два процессора, если это доступно, чтобы работать е этим запросом. - - Имеются некоторые затраты, функционирования, еввааниые с использованием средств вне сервера, чтобы обработать запрос, жо в зависимости от того, насколвко большие данные установлены, использование интегрированных разделенных представлений может создать громадные выгоды в функционировании. Очевидно, мы ие будем использевать это, чтобы хранить простой список партнеров, но когда вы размещаете информацию о сеансе с полумиллионом активных пользователей Интернета одновременно, это может быть очень хорошей платой за распределение ваших данных по нескольким серверам. Дополнительную информации, см. в "Professional SQL Server iOOO Programming" Роба Виейры (Wrox Press, ISBN 1-861004-48-6!. Хранимые процедуры Хранимые процедуры — в основном простые программы, размещенные в БД. Основное различие между хранимыми процедурами и пакетами заключается в выполнении; хранимые процедуры предварительно компилируются, В этом разделе мы конкретно рассмотрим, как создать хранимую процедуру, точнее, некоторые особенности хранимых процедур» которые интересны при реализации СУВД. Хранимые процедуры очень похожи на определяемые пользователем функции е иеекоя ькими важными различиями: □ Хранимые процедуры возвращают наборы результата в очень свободной манере, когда мы не должны объявлять то, что будем возвращать клиенту, и можем возвращать любое число результатов. □ Хранимые процедуры могут иметь несколько выходных параметров, в то время как скалярные функции могут иметь только одно возвращаемое значение любого типа или могут не иметь выходного параметра,
□ Скалярные функции могут использоваться а скалярных выражениях, в то время как хранимые процедуры — не могут» □ Хранимые процедуры позволяют нам изменять ВД (в форме обновления, изменения уставок и т. д.), в то время как функции не могут изменять ничего во время выполнения. Каждое иа этих различий определяет, когда использовать хранимые процедуры вместо функций, особенно, последние два. В остальной части этого раздела мы рассмотрим: □ возвращаемые хранимыми процедурами значения; □ обработка ошибок; □ использование хранимых процедур для организации связи между нашими приложениями; □ использование хранимых процедур в качестве механизмов безопасности; □ использование хранимых процедур для решения общих проблем. Независимо от общей архитектуры, хранимые процедуры — особенности ВД, которые каждый SQL-i;porpaMMHCT должен использовать и широко использует. Написание эффективных хранимых процедур требует много навыков и опыта, так что неплохо экспериментировать на фиктивных даиных и испытывать различные методы создания. Возвращаемые хранимыми процедурами значения Имеются три пути, которыми хранимые ироцедары могут связываться е внешним миром: □ возвращаемые значения — обычно возвращают значение состояния; □ выходные параметры — обычно возвращают отдельные значения параметров; □ наборы результатов — обычно возвращают таблицы информации. Каждый из них имеет вполне определенную цель, которую мы рассмотрим в следующих разделах. Возвращаемые значения В отличие от определяемых пользователем функций оператор RETURN хранимой процедуры может возвращать только значение целого числа, которое обычно используется, чтобы указать состояние. Следующая таблица дает общие указания для протокола возвращаемого значения с объяснением каждой величины: Значение Описание > 0 Процедура успешно выполнена с некоторым значением, которое может быть выведено, используя определение процедуры. 0 Процедура успешно выполнена, но никакая дополнительная информация не рассматривается. -1 + -99 Процедура потерпела неудачу с системной ошибкой. Не может быть выведено значение, отличное от значения возникшей системной ошибки. В настоящее время используются только значения от -1 до -14.
Значение Описание -100 Основная определяемая пользователем ошибка, которая ие может быть отработана в Transact-SQL. < -100 Определяемое пользователем сообщение об ошибке, основанное па том, что произошло в хранимой процедуре. Вы не должны использовать значения о» —1 до -14 для возвращаемой информации хранимой процедуры. Эти значения используются в SQL Server. Причина для такого протокола довольно проста. Если возникает любая ошибка в хранимой процедуре, и вам нужен сигнал ее возникновения, то простая проверка, что возвращаемое значение отрицательно, означает наличие проблемы, а неотрицательное возвращаемое значение означает успешную работу. Например, возьмем следующую простую процедуру, которая возвращает целое значение, переданное ей: CREATE PROCEDURE returnValueStesu SreturnThia int - 0 I | RETURN gteturnThis Если мы выполним эту процедуру, задав значение переменной: I DECLARE IratuenValue int EXEC eret-uxnValue = returnValeeStest gretuEnThis =» 0 SELECT greturnValua AS raturnValue ... мы получим следующий результат: retumValue О Следующая кренимая процедура создана, чтобы использовать значение первой процедуры: I CREATE PROCEDURE callerfitestReturnValue - ( BteatValue int ) AS DECLARE irecumValue int EXEC greturnValue. - returnValueStest gtestvalue SELECT greturnValue AS returnValue Продолжение кода на следующей странице
I IF greturnValue < 0 — отрицательное значение - оиивка BEGIN SELECT 'Произошла ошивка: ' f CAS"IfreturnValus AS varchar(10}J AS etatae END ELSE значение равно нуле или успешная вавота BEGIN SELECT 'Вызов успешен:. f + CAST(greturnValue AS varchar(10)} AS status END GO Выполнение ее e отрицательной величиной: £ EXEC callerSteatReturnValue -10 ... сообщает нам, что произошла ошибка: retumValue -10 status Произошла ошибка: -10 Очевидно, если мы изменим параметр на неотрицательное число, то получим сообщение, что никакая ошибка не произошла. Выходные параметры Ъыхот^зле параметры позволяют нам вернуть скалярное значение любого типа при вызове процедуры, и позволяют передать, если необходимо, данные типа cursor. Выходной параметр — лучший способ возвратить скалярную величину из одной процедуры в другую, так как считается плохим тоном использовать возвращаемое значение для агой цели, даже если значение, которое мы возвращаем, окажется целым числом. Мы можем иметь столько выходных параметров, сколько нужно. Используйте следующий синтаксис: I DECLARE evariable int EXEC storedProcedure ((variable = fvariabi* ОСТРОТ Обратите внимание, что вы можете увеличить производительность в приложениях SQL Server, используя выходные параметры, чтобы возвратить единственную строку, что устраняет накладные расходи для получения набора результатов. Наборы результатов Возвращение наборов результатов клиенту столь же просто, как включение оператора SELECT в ваш код: I CREATE PROCEDURE testSstaceiBenr. AS SELECT 'Hi' AS hiThere SELECT 'Bye' AS byeThere GO
Выполнение агой хранимой процедуры: | EXEC testfstatewent возвратит два набора результатов hiThere Hi byeThere Bye Это кажется очень простым, но имейте в виду, что так как хранимые процедуры — заранее компилируемые программные коды, они должны выть автономными. Они не должны содержать код, который требует взаимодействия с клиентом — или человеком, или другой программой. Хранимые процедуры — сажи и© себе не являются транзакциям и. «ели соединение разрывается на пол пути выполкещгя хранимой процедуры, остальная часть процедуры не будет ни закончена, ни будет выполнен откат. Вот почему ныходные параметры и возвращаемые значения недоступны вызывающему, пока все наборы результатов яе были получены или отменены, и хранимая процедура закончена. Обработка ошибок Обработка ошибок в коде SQL Server — один иа его самых слабых пунктов. В SQL Server нет никакой организации ловушек ошибок. Однако мы всегда можем определить, вызвал ли оператор ошибку, выполнив соответствующие действия. Рассмотрим, например, следующий вызов несуществующей хранимой процедуры: Ц EXEC dba„doesntExisG Он возвратит следующее стандартное сообщение об ошибке: Server: Msg 2812, Level 16, State 62, Une 1 Could not And stored procedure 'dbo.cloesntExist'. (Сервер; сообщение 2812, уровень 16, состояние 62, строка 1 Не удается найти хранимую процедуру 'dbo.doesntExist.) Если мы посмотрим в таблице master. dbo. sувюеssages значение для ошибки е номером 2812: I SELECT error, description FROM master.dbo.sysmessages WHERE error - 28X2 ... мы увидим сообщение с полным статическим текстом, необходимым, чтобы построй^ сообщение об ошибке, и признак для размещения значений параметра: error description 2812 Could not find stored procedure
Мы можем также сформировать ошибку в коде, используя оператор RAISERROR (формирование ошибки), который мы раньше использовали, чтобы послать клиенту сообщение об ошибке. RAISERROR — очень мощное и асное средство, и мы можем делать с ним намного больше. Мы можем использовать команду RAISERROR, чтобы создать наши собственные сообщения и сохранить их в таблице sysmessages. Важно упомянуть о гибкости RAISERROR. Она имеет встроенную языковую поддержку, так что если вы должны интернационализировать ваш код ВД, то можете создавать версии ваших сообщений на других языках. Соответствующее сообщение будет отображено в соответствии с настройками языка пользователя. Используя RAISERROR, мы можем: □ возвратить шрегламентирааанное сообщение; □ возвратить сообщение, что мы храним в таблице master. dbo. sysmessages со специальным форматированием; □ возвратить номер ошибки (между 50000 и максимальной величиной типа bigint); □ возвратить серьезность ошибки, которая сообщает нам, как плохо сообщение, от "informational” (информационное) да "hardware corruption" (повреждение аппаратных средств); □ возвратить значение состояния, которое указывает источник, где возникла ошибка. Самый простой синтаксис для команды RAISERROR: Ц RAISERROR <номер__о1аибки> <сеовиение> Это позволяет нам определить номер ошибки и некоторый текст, описывающий ошибку. Значение сообщения может выть значении типа varchar или содержанием переменной типа varchar. Например: 9 RAISERROR 311911911 'Это сообщение-тест' возвратит: Server: Msg 911911911, Level 16, State 1, Line 1 Это сообщение-тест Так, в наших хранимых процедурах мы можем создать оператор, который проверяет, является ли значение выходного параметра меньше нуля, и если это так, формирует ошибку: I create PROCEDURE fcestSvalidatePaxm ( SparmValue int J AS IF gparmValue < 0 BEGIN DECLARE Gmsg varchar(1QD) SET Gir.ag ~ 'Неправильное значение SparmValue; ’ + CAST(IparmValue AS varchar(10JJ +
', Значение должно быть неотрицательное.' RAISERROR 50000 8твв RETURN -100 END SELECT 'Тест успешно выполнен.' RETURN 0 Тогда, если мы передадим 0 для проверки: Ц dbo.teatsvalidateFa rm Q то получим: Тест успешно выполнен. Если мы передадим отрицательное число, то получим ошибку: Server: Msg 50000, Level 16, State 1, Procedure testSvalidateParm, Une 11 Неправильное значение @parmValue: -1, Значение должно быть неотрицательное. Другой случай, когда полезно включить сообщение об ошибке —после операторов INSERT, UPDATE или ПЕЬЕИ. Причина — та же самая. Вели возникает ошибка, мы можем точно определить, какой оператор вызвал отказ. Пусть мы имеем следующую таблицу: I CREATE TABLE test testld int IDENTITY, name varchar(fiO> NOT NULL COHSTBAIST JSKtest narfte UNIQUE ) Sflecs создается таблица test (тестированиеЛ которая содержит два столбца; автоинкрементный test Id (идентификатор теста) типа int и паде (название) типа varchar(60). Сформируем следующую хранимую процедуру, чтобы создавать нояые значения: CREATE PROCEDURE teatSina ( - - : Bname varchar (БО) J ' AS ------ INSERT INTO dbo,test(пале) VALUES (8name! IF terror <> 0 - - BEGIN - . — формирует ошибку, которая сообщает пользователю, что -рсизолло. RAISERROR 53Q0Q 'Ошибка размещения данных в таблице test' END . - ... GO . -
а затем выполним оператор: Щ ЕХ8С dfao.testSins gname “ NULL Результат будет следующий: Server : Msg -515, Level 16, State 2, Procedure testSins, Line 6 Cannot insert the value NULL into column 'name’, table ’master .dbo.test', column does not allow NULLs, INSERT fails. Server: Msg 50000, Level 16, State 1, Procedure testSins, Line 10 Error inserting into test table The statement has been terminated. (Сервер: сообщение SI 5, уровень 16, состояние 2, процедура ieettine, строка в Нельзя поместить значение NULL в столбец 'пате', таблица. 'iaa»terMo.tegf, столбец не допускает значения NULL. Оператор INSERT потерпел неудачу. Сервер: сообщение 50000, уровень 16. состояние 1, процедура test Uns, строка 10 Ошибка размещения данных, в таблице test Оператор был удален.) Может показаться, что это делает код более грязным, поскольку мы мзда- имеем обработчик и ошибок, но важно проверить код возврата и статус ошибки после каждой» оператора корректировки или вызова хранимой процедуры, Вели мы это делаем, то можем увидеть, какая ошибка произошла, и точно знаем где. Это очень полезно, так как могут вить случаи, когда вместо размещения одной строки в одну таблицу, мы могли бы разместить несколько строк в таблицу из приложения вашего клиента, или могли бы вызывать эту хранимую процедуру из другого сервера. Такой вид обработки ошибки будет экономить время отладки. Имеются две операции, которые мы можем выполнить, чтовы улучшить формирование сообщений об ошибках в SQL Server: □ Разместить еоовщеийя об ошибках ВД, чтобы отобразить сообщения ов ограничениях в более значащих конкретных сообщениях. □ Создать формат тега сообщений об ошибках, чтобы возвращать болте полные сообщения клиенту. Схема ограничений В этом разделе мы рассмотрим технику размещения ошибок, формируемых нашими ограничениями для получения более ясных сообщений, что- может быть в значительной степени автоматизированным процессом. Стандартные сообщения SQL Server об ограничениях не предназначены для использования конечным пользователем. Рассмотрим случай, где мы пробовали ввести значение NULL в столбец — в данном случав в столбец name (название) таблицы test (тестирование). Server: Msg 515, Level 16, State 2, Procedure testSins, Une 6 Cannot insert toe value NULL into column 'name', table 'master.dbo.test', column does not allow NULLs. INSERT tails. (Сервер: сообщение SIS, уровень 16, состояние 2. процедура tettfint, строка в Нельзя поместить значение NULL в столбец 'пате, таблица 'matter.dbo.test', столбец не допускает, значения NULL, Оператор INSERT потерпел неудачу^
Анализируя основную информацию из этого сообшения (номер ошибки, который сообщает нам, что это нарушение использования NULL, а также столвец и таблицу из сообщения): Server: Msg 515, Level 16, State 2, Procedure testSins, Une 6 Cannot insert the value NULL into column 'name’, table 'master.dbo.test', column does not allow NULLs. INSERT fails. ... мы можем сформировать следующее сообщение об ошибке, которое намного лучше для пользователя: Вы должны ввести значение в столбец пате таблицы test. Нарушения ограничений использования NULL довольно просто идентифицировать и формировать сообщения, но как относительно других ограничений? Рассмотрим ограничения-проверки. Предположим, что мы добавляем ограничение-проверку, чтобы запретить пользователю Bob доступ к таблице test: . - . I ALTER IABLE.dbo.test ADD CONSTRAINT "ktssESnamSBSxingSnotEqualToBob CHECK (пегие <> 'Bob’) Если теперь пользователь попытается ввести имя Bob: I INSERT МИО dbo. test (нале) VALUES f'Bob'J : результатом этого будет стандартное сообщение об ошибке SQL Barver: Server: Msg 547, Level 16, State 1, Line 1 INSERT statement conflicted with COLUMN CHECK constraint . chkTestSname$5t'irigSnctLqua!To0ob'. The conflict occured in database ‘master", table ‘test’, column 'name'. The statement has been temiinated. (Сервер: сообщение B47, уровень 16, состояние 1, строка 1 Оператор INSERT конфликтует е ограничением COLUMN CHECK 'chkTest$name$gtring$notEqualToBob', Конфликт возник в БД 'master, таблице 'test', столбце 'пате. Оператор удален.) Мы предпочли бы возвратить специальное сообщение, что пользователь по имени Bob попытался обратиться к таблице. Стандартное сообщение об ошибке’ отрывочно, потому что мы только вернем определение ошибки, но мы межей использовать э го как основание для специального сообщения, Чтобы сделать это, мы используем новые расширенные свойства, которые обсуждали в главе 10.. В этом случае мы создадим свойство для ограничения, которое объявляет сообщение, что мы хотим запретить пользователям просмотр: IEXEC sp_addexendedptoperty Inaise — 'ErrorMessafe', - - evaiue - 'Вы не можете обращаться к таблице test’, eievelfltype - ' Ueer*, Sieve! р-аг-л '-dbo', '' tiewlltype - 'table', tlevellhaiae - 'test', eievelZtype - 'constraint', вШгвХапашв » ' chkTestSnwseSвtringSnotBcIualToBob,
Как только расширенное свойство помещено в ВД» SQL Server выводит нормально® сообщение, когда возникает ошибка. Механизм клиента перехватывает это, выделяет название ограничения и запрашивает расширенное свойство, чтобы получить специальное сообщение, которое затем посылается клиенту. SELECT value AS -e;:oaqe FROM ::FN_LISTEXTENDEDPRCPERTY('Errox«e3sage', 'User', 'dbo', 'table', ’test', 'constraint', * chkrestSnaneSstringSnotBquslToBob' J Итак, пользователь увидит следующее новое сообщение: message Вы не можете обращаться к табдаща test Эта модель может выть расшарена, чтобы сделен» ее намного более гибкой, автоматически устанавливая сообщения для каждого из возможных ограничений. Тогда мы можем отобразить каждое сообщение реальным сообщением для клиента. Хотя, одна вещь... Я обычно предиочитаю иметь метод на клиенте, чтобы видеть стандартное SQL Server сообщение об ошибке, так что я могу видеть то, что действительно произошло (например, дополнительная информационная кнопка в диалоговом окне сообщения). Этот метод стоит дополнительного путешествия к серверу и обратно. Клиентская часть объекта могла бы быть закодирована так, что когда мы разрабатываем наши приложения, то идем к серверу, чтобы обрабатывать сообщения об ошибках, но когда это используется в окончательной версии нашего программного обеспечения, то помещаем сообщения об ошибках в DLL для более быстрого чтения. Теп сообщений об ошибках Вели мы имеем размещенные сообщения, которые SQL Server посылает нам, важно удостовериться, что сообщения, которые мы посылаем назад клиенту, максимально полезны. При создании нврегломен-ишроваммы* сообщений мы очень ограничены тем, что можем возвращать в сообщении. Я добавляю кодируемый тег в конец сообщения: I'текст_соо6шения* + '<имя_®вга_1-зкачваие_вега_1;имя_твга_2-аааченив_»вга_2;’ + ' . . .; имя_теra_N=- знач*ние_тега_Н>’ Здесь каждый тег указывает значение, которое может быть важно для клиента программного обеспечения, такое, как; оЬд Объект, который формирует ошибку (Р — procedure — процедура, TR — trigger — триггер и т. д.). type Тип объекта, который формирует ошибку. call Предварительный вызов действия, которое вызывает ошибку. col Значения столбца, которые вызывают ошибку. key Значения ключа строки, имеющей проблемы. Это могло бы использоваться, если весь пакет вызовов хранимой процедуры был послан к SQL Server, и мы должны дифференцировать их.
action severity occurrence Специальное действие, которое вызывающий должен выполнить, чтобы восстановить проблему. Серьезность сообщения, подобно "warning” (предупреждение), "severe" (серьезное) и т. д. Число раз, какое может произойти выполиеиие одного и того же типа. В наших примерах мы для простоты будем использовать теги obj, type и action. Например, в блоке каждой хранимой процедуры, где мы объявляем наши переменные (такие, как переменные, чтобы поместить число строк, сообщения и т. д.), мы имеем следующее: — используется, чтобы разместить первую часть заголовка сообщения DECIARE imsgTag varchar(2551 — открываю»<й тег соовшения с именем объекта и его типа SET вшздТад = ' <ebj-’ * object_t»Me [вBpxocld) + ';type»P,-' Тогда всякий раз, когда нам нужно сформировать ошибку, мы формируем вызов следующим образом: В SET 0toscj » 'Ad hoc message' + ImsgTag + action» (insert cest)>r | RAISERROR 5 ОС 10 gMJ и он возвратит сообщение, наподобие следующего: Server: Msg 50000, Level 16, State 1, Une 5 M hoc message<obj=testSins;rype=P;action“(insert :est)> {Сервер: сообщение 60000, уровень 16, состояние 1, строка 5 Нерегламентированюе сообщение <оОъект — testSins; тип — процедура; действие (размещение теста )>) Это сообщение позволяет проще точно выяснить, что произошло и где .это произошло в хранимой процедуре testSins. Выделение текста между сковками < к >, а также анализ каждого значения тега, чтобы сформировать более удобочитаемое сообщение пользователю, является также-простым. Теперь мы можем сообщить клиенту, является ли наше сообщение только предупреждением, или это что-то более серьезное. Этот метод имеет то преимущество, что не требует никакого дополнительного кодя, Рассмотрим следующую хранимую процедуру. Она моделирует ошибку, задавая переменной Serror (которую мы обычно используем, чтобы разместить значение сбег гот) значение, отличное от нуля: I CREATE PROCEDURE testSerrorHeasage ( ekey_testld int ) ..... AS — объявление переменных, жаояьзувмгх для обработки’ожйок в блоках DECIARE йеггог int, -- ислользувтая для размещения зиаче-вия ошибки Продолжение кода на следующей странице
Imsg varchar(255), BmsgTag varchar(255? — используется для формирсааюм — соо&веиия об ошибке — используется для раэививния тегов — сообщения ОО оиЛк* — задание тега сообщения ов ошибке SET OmsgTag - ’<т + objecc_name(ggprocld) + 'ftype-P' + 'fkeyvalue»' '8key_routineld:' + CONVERT (varchar (10), Skey_testld) — выэоа какой-жво хранимой процедуры или оператора обновления для — какой-либо таблицы . — получение номера строки и уровня ошгбки для кода обработки ошибки SELECT Jerror = 100 -- моделируемая ошибка IE terror Q -- ошибка возникла вне этой процедур*» BEGIN SELECT gmsg « 'Проблема возникла при вызова nonExistentProcedura. ' + fcesglag + call” (процедура nonExistentPrC-cedore) >' FAISERROR 50000 8®sg RETURN -100 END Эта процедура довольно дшпша, однако, все, что она делает, это формирует ошибку! К сожалению, начти весь этот код (кроме кода формирования -тегов ошибки) обычно требуется, когда мы вызываем другие хранимые процедуры или делаем изменения в других таблицах. Мы должны проверить возвращаемое значение и значение Merror на каждом шагу выполнения кода. Выполнение этой процедуры: Ц tesrSerrorMessage ekey_testld - 15 возвратит следующее сообщение Server: Msg 50000, Level 16, State 1, Procedure testSerrorMessage, Une 23 A problem occured calling the nonExistentProcedure, < test $err or Message: type=P;keyvalue=@key..routine Id: 10:cail=(procedure nonErtstentProcedurep (Сервер: сообщение 50000, уровень IS, состояние l, процедура teetSerrorMeesage, строка 23 Проблема возникла при вызове noaExMentProcedure, <test$errorMessage;type^P;^ytMlue—@}uy_nmtineMilO;eaU—(процедура nanExtstentProcedure)> ) Оно передает обслуживающему персоналу несколько данных. Во-первых, здесь, вероятно, выла ошибка, возникшая перед этим сообщением, которая сообщила нам, какова реальная проблема. Во-вторых, nonExistentProcedure была источником проблемы, -— это было получено в процедуре test 5е г го rites sage, и значение параметра Stey_routineld было равно 10. Инкапсуляция Инкапсуляция SQL-кода для пользователя (сокрытие от пользователи) — одна из наиболее важных причин использования хранимых процедур, позволяющих нам выполнить много команд с помощью единственного вызова хранимой процедуры. Это интересно по двум причинам:
□ Хотя результаты, действия могут изменяться, само действие не изменится. Другими словами, если код, который мы написали, работает, фактическая реализация не имеет значения. Таким образом, мы можем настраивать внутреннюю работу хранимой процедуры без необходимости изменять интерфейс, который видят внешние пользователи. Мн могли бы даже изменить все вмена основных таблиц и структуры, а хранимая процедура будет все так же работать. О Интерфейс может выть построен, полностью инкапсулируя SQL-вызовы хранимых процедур. В этом случае программы клиента никогда не будут включать никаких хитрых запросов в SQL к серверу БД, ови будут просто иметь возможность использовать хранимые процедуры. Это объясняет, какие действия могут быть - выполнены, так как мы можем видеть список возможных процедур. Я попробую продемонстрировать первый момент на быстром примере. Предположим, что вы просите написать процедуру, которая получает имена клиентов из таблицы person (человек), и вам дали следующие спецификации: □ имя -- pexsonSgot; □ параметры — gpersonld □ наборы результатов — единс-венн ый результирующий набор, содержащий строку из таблицы person, включающую personid, firstSane и lastdame со значением personid, отфильтрованным параметром Bpersonld. Вы могли бы написать следующий код: CREATE PROCEDURE perscnSget . ( gperec-nld int ) AS SELECT personid, firstName, lastSarae FROM person | WHERE personid - gperaonld Если, однако, начать работать только в мире SQL-программирования, мы могли бы напивать следующее: CREATE PROCEDURE personSget ( , , врег son I d int- . - 1 AS CREATE TABLE «person " ~ ''' ' ' ( - , personid int flrBtKaee varchar(301 lastNaraa varchar(30) - ... . ) DECLARE ^cursor cureor, вс_рекволХа int, 8c_f LrstWatae vaxchaxOO)., - 8c_laetKaine varchar (30) :. Продолжение кода на следующей странице
SET в-cursor - CURSOR FAST_FORWARD FOR SELECT pereoald, firsENae», last Warne FROM person OPEN icuraor FETCH NEXT FROM Icursor INTO icjetsonid, rc__rirstName, 0c_lastName WHILE ggfetch_statua - 0 BEGIN IF Spersonld « 1 BEGIN INSERT-INTO ♦person - VALUES (0cjperswiId, gc_firatName, €.c_2a«tHa«e) END FETCH NEXT FROM ecursor INTO i st aid, вс_£ iratNane, ec_lastName END SELECT * FROM fperson Хотя этот код написан в плохом стиле SQL, онработает. Процедура возвратит надлежащие значения, и если количество данных невелико, ее выполнение даже не будет слишком плохим. Однако при инкапсуляции запроса легко нерепиеать этот код в соответствующем виде, показанном ранее. Следует добавить, что программное обеспечение клиента, использующее эту хранимую процедуру, яе изменяется. Последний момент, который я хочу отметить, — это то, что, используя хранимые процедуры, мы даже ие нуждаемся в фактической структуре таблицы, чтобы начать создавать приложения. Используя некоторую форму генерации кода, мы должны быть способны создать набор методов (хранимых процедур) для нашей БД, которые ничего не делают, но предлагаются программисту, так как их достаточно для начала. Обычно называемые фиктивными процедурами, они часто используются, чтобы проверить интерфейс пользователя, прежде чем все полностью реализовано. В предыдущем примере мы могли построить следующую фиктивную процедуру, чтобы - начать работу программиста: CREME PROCEDURE personSaet I ‘ gpersonld int Ila | SELECT 1 AS personld, 'Joe' AS firstName, 'Jones’ AS lastName Хотя эта процедура я не делает ничего и даже игнорирует параметр, она возвратит строку, которую прикладной программист мажет использовать, чтобы ввдеть, работает да экран, когда написан соответствующий код. Что важно, так это то, что внешний интерфейс ие нарушается. Безопасность Как и в представлениях, безопасность в хранимых процедурах осуществляется на уровне объектов. Лишь потому, что пользователь может использовать хранимую процедуру, не обязательно, что он/она должен иметь права доступа к каждому объекту, к которому обращается хранимая процедура. Мы можем использовать хранимые процедуры как наш первичный механизм безопасности, требуя, чтобы весь доступ к серверу выполнялся через них, Строя процедуры, которые инкапсулируют все функциональные воамажности, мы можем использовать разрешения доступа к хранимым процедурам, чтобы ограничить то, что пользователь может делать.
Это позволяет нам иметь ситуационный контроль доступа к таблице, Другими словами, мы могли вы иметь две различных процедуры, которые выполняют точив те же самые действия, но предоставление прав пользователю на одну процедуру не подразумевает, «то он/она имеет права на другую. Так, если экран построен, используя одну процедуру, пользователь может быть способен использовать это, ио если только экран не использует другую процедуру. Наличие двух процедур, которые выполняют точно те же самые действия, дв.теко от совершенстве, но это иллюстрирует факт, что мы можем строить объекты, которые имеют доступ к одним и тем же таблицам различными способами, и мы можем обеспечить доступ независимо от основных объектов, Можно иметь итоговую процедуру, которая дает список людей, чьи имена начинаются с буквы Д, и которую пользоватеи» может использовать, а также процедуру, которая формирует список имен пользователей, которая ему/ей недоступна. Подобная ситуация может быть реализована не только с представлениями, но, конечно, и с процедурами, когда можно не только просматривать данные, но и также создавать, изменять и удалять их. Более подробно мы рассмотрим использование безопасности в SQL- для блокировки доступа к нашим объектам позже в даниой главе. Счетчик транзакций Кроме очевидных выгод от инкЕшсуляции SQL-кода для пользователя и установления : модели безопасности, хранимые процедуры могут использоваться для подсчета текущих транзакций — очень важный фактор при использовании транзакций. Счетчик транзакций (обозначается через SStrancount) сообщает нам, сколько активных транзакции в настоя шее время опщьего. Когда мы используем транзакции в натпих пакетах кода, то должны быть очень осторожны, чтобы открывать и закрывать их перед завершением пакета и не остетлять никаких блокировок, которые могут влиять на других пользователей. Независимо от того, каким был счетчик транзакций, когда вы начали процедуру, он должен иметь то же самое значение, когда, вы завершаете процедуру- Эта концепция имеет важный смысл и делает кодирование содержательным. Возьмем следующие две процедуры: CREATE PROCEDURE -ra*Test$in"ier AS BEGIN TRANSACTION — код определяет ошибку, приводящую к откату ROLLBACK TRANSACTION RETURN -100 ' CREATE PROCEDURE trar.TestS-zwter AS DECLARE exwtValtt* int BEGIN TRANSACTION EXEC gretValue - tranlestfiinner IF 9 re-„Value < - 0 BESTS ROLLBACK TRANSACTION - - ИЕТОВИ iretvalue END b COMMIT TRANSACTIOS - 17 1868 WO
Ч-обы посмотреть, что произойдет, ж рассмотрим следующий запрос и будем медленно выполните его. Это может быть болезненный процесс, но во многих случаях он является единственным способом полностью понять код: Ц БХЕС tranTestSouter 1 tranTestSouter DECIARE tretValue int Задает переменную для размещения возвращаемого значения из процедуры @retValos = NULL 2 tranTestSouter BEGIN TRANSACTION Начинает новую транзакцию e8t-rancwjnt = : 3 tranTest$outer EXEC IreWalue = tranTestSinner Вызывает процедуру tranTestSinner 4 tranTestSinner 5EG IN TRANSACTION Начинает вторую транзакцию Mtxancount - 2 S tranTestSinner ROLLBACK TRANSACTION Откатывает все транзакции Mtrancount в t rantes 1$ inner RETORN -100 Возвращает вызывающей процедуре отрицательное значение, чтобы сообщать, что что-то произошло ОтаВКА: счетчик транзакций язмекилея 7 tranTestSouter После возврата значение iretvalue заполнено BretValue » -ieo 8 tranTestSouter IF flretValue < 0 Проверка, возникла ли ошибка 9 trantest$outer ROLLBACK TRANSACTION Откатывает все транзакции ОШИБКА; eetrancounr ражен нулю уже Так как счетчик трвнзакдий изменен между шагом 3, когда мы начат процедуру, и шагом в, когда закончили ее, то получим следующее сообщение ов ошибке: Server; Msg 266, Level 16, State 2, Procedure tranTestSinner, Une 6 Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement Is missing. Previous count = 1, current cowrt - 0. (Сервер: сообщение 266, уровень 16, состояние 2, процедура tranTestSinner, строка 6 Счетчик транзакций после EXECUTE указывает, что пропущен оператор COMMIT или ROLLBACK TRANSACTION. Предыдущий счетчик — 1, текущий счетчик 0.)
Неблагоразумно добавлять код в хранимую процедуру, чтобы проверить, быя ли счетчик транзакций изменен другим процессом, так что мы должны заново продумать проблему. Вместо использования прямых транзакций, мы начнем новую транзакцию, когда начнем хранимую процедуру, а затем установим точку сохранения транзакции. Обратите внимание, что мы устанавливаем переменную для точки сохранения с именем процедуры плюс уровень вложенности, так что оно будет уникально независимо от того, как глубоко мы вложили вызов нашей процедуры или триггера: BEGIN TRANSACTION SAVE TRANSACTION etranPoint DECLARE etranPoint sysname — тип данных для идентификаторов SET etranPoint object=neae(e8procld) * CAST (tf nestlevel AS varchar (10} ) Итак, мы перекодаруем наши процедуры следующим образом: CREATE PROCEDURE rranTeetSinner AS DECLARE tt-ranPoint sysnarae- - — тип данных для идентификаторов SET etranPoint = objaetynaireitgprocld) + CAST (fieneatlevell AS varchar(10)) BEGIN TRANSACTION SAVE TRANSACTION Itrenroint — снова ничего не делаем для простоты ROLLBACK TRANSACTION etranPoint COMMIT TRANSACTION RETURN -100 GO CREATE PROCEDURE tranleetSoutex AS DECLARE IretValue int : DECLARE etranPoint аувпаяв - — wo данных для идентификаторов SET gtranPoint * object_name (вВргос1сП + CAST (Mneetlevel) AS varchar(10}) BEGIN TRAMSACTIOS SAVE TRANSACTION (tranPoint EXEC SretValue » tranTestSinner IF (JretValue < 0 BEGIN ROLLBACK TRANSACTION etranPoint COMMIT TRANSACTION - RETURN GrerValue END C<»MIT TRANSACTION GO Если мы запустим наш предыдущий пример Ц EXEC tranTeatSouter 4Q7 в:
1. tranTestSouter DECLARE вretValue int переменную для размещения значения из процедуры к i g;T- ' ,:v , у Ы 1Ф;-г у ЗЯ ' 1) 2 tranTestSouter DECLARE gtranPoint sysname SET 8 7. ran S’; zz. z = obj estjBM {t gprocld) + CAST(gfnestlevel AS varchar (10П сохранения транзакции 98t ranRoiг. t ’tranTestScuterl' 3 tranTestSouter 5E S 2 M AST z Начинает новую IT'Z Z'_!‘ ” - 1 4 tranTestSouter SAVE TRANSACTION 'tranTestSouter!' Сохраняет но не изменяет Bit ranOTtmt «trancount = 1 S tranTestSouter EXEC flretValue = tranTestSinner Вызывает процедуру tranTestSinner в tranTestSinner DECIARE etranPoint sysname SET ttranPoint - object_naine (ggprccld) + CAST(Mnestlevel AS varchar(10)) сохранения траязакцви ggtranPoint ' tranTe s t-S i nne r 2 ’ 7 tranTestSinner BEGIN TRANSACTION Начинает вторую wjftiraricounu « 2 8 tranTestSinner SAVE TRANSACTION 'tranTestSinnerZ' тряиздкцню f но не изменяет ittrancount 88trancount = 2 9 tranTestSinner ROLLBACK TRANSACTION 'tranTestSinner2' Откатывает к точке tranTest$inner2 Mtrancount - 2
10 tranTestSinner - COWitT TRANSACTION Завершает транзакцию и возвращает eftrancount к значению, когдамы начаии процедуру на шаге б Bitrancount = 1 11 tranTestSinner RETURH =100 Возвращает отрицательное значение вызывающей процедуре, чтобы сообщить, что что-то произошло 12 tranTestSouter После возврата значение tretValue заполнено OretValue « -100 13 tranTestSc-uter IretValue < 0 Определение, произошла ли ошибка 1 14 t ranTestSouter ROLLBACK TRANSACTION ' traгl'XestSouterl, Возвращает к точке сохранения, не изменяя getrancount Mtrajicoun t = 1 15 tranTestSouter c Завершает конечную транзакцию Mtrancount — 0 18 tranTestSouter RETURN fretValue Возвращает значение внешнего вызова вызывающей программе Этот код помогает оградить нас от какой-либо защиты транзакции и, как результат, от возможтестн иметь неправильные сохраненные данные после откате транзакции — потому что, как мы видим,-кед во второ* храяиагой процедуре вовсе ве прекращает выполнение иа-за ошибки счетчика транзакций. Изменения кода, которые мы сделали, на самом деле связаны только с внутренней хранимой процедурой и таковы, что ошибка внутренней транзакции ве должна нарушить внешнюю транзакцию. Ошввкй внешнем транзакции не является проблемой, так как fitraneoont обратится в нуль, величину» с которой этот параметр и нвчияался.
Положительный побочный эффект всего этого — то, что мы имеем возможность позволить выполнить внешнюю транзакцию, Имеются такие случаи, когда мы хотели бы ааверпшть часть транзакции, когда какая-то часть ее вызвала ошибку, и приведенная гибкость программы позволяет вам это сделать. Общие способы работы с хранимыми процедурами Фактическая цель этого раздела — дать основные блоки создания общих хранимых процедур — материал, который я редко видел в печати или в Интернете. Мы исследуем эти процедуры и обсудим некоторые "за" и "против" каждого решения. Хранимые процедуры — отличные средства, хотя, как увидим, напоминают ситуацию, когда мы вынуждены забивать квадратный гвоздь в круглое отверстие. Создадим на тли объекты, чтобы иметь доступ до следующей таблицы: Здесь создаётся таблица routine (программаJ с четырьмя столбцами: автоинкрементный столбец rcutineld (идентификатор программы) типа int, являющийся первичным ключом; пте (название) типа varchar (384), не может принимать значение NULL, содержащий уникальные значения; description типа varchar (100), не может принимать значение NULL и ts_timestamp (время создания/обновления) типа time-stamp. Чтобы загрузить некоторые исходные данные, мы выполним фрагмент: Здесь мы заменим <БД> именами наших ВД. В моем примере я буду использовать Northwind, Master и Pubs с общим количеством примерно 1000 строк. Я выполняю эти примеры на портативном компьютере Pentium П 400 с 2вв Мб ОЗУ — довольно устаревшем компьютере, Вее ато дает нам основание для некоторого основного испытания работы хранимой процедуры, так как мы рассматриваем возможные способы построения хранимых процедур для наших приложений. Восстановление данных Этот код заполняет список элементов управления в приложении и является обычной процедурой в моих приложениях. Каждый из параметров является фильтром списка, так что пользователь может просматривать список по своему усмотрению. Хранимая процедура следующая:
CREATE PROCEDURE rowtineSilst ( Sroutineld int - SULL, -- первичный ключ да*.определен”я — единственной строки впале verchar(60> = ' %’, — так же, как В routine.паже edescription varchar(100} - • %' — так же, как в routine.description ) — Описание : получает записи программ для отображения а списке — Возвращаемая величина : неотрицательная - успеино* выполнение,* : от -1 до -99 - системная ошибка в качестве выходной величины; : -100 - ошибка, формируемая процедурой AS — так как сообщения о сумме будут непонятны для клиентов SET NOCOUNT ON — по умолчанию параметр впаиж - ' , если передаваемая величина - NULL IF ename IS NULL SELECT gname - ’ %' — по умолчанию параметр вdescription - ' если передаваемая величина - NULL if Sdescription is null select edescription • ' 1' — выделение ие таблицы всех полей (кроме ts_timesterap) для просмотра SELECT routine.routineld AS routineld, routine.name AS nam*, routine.description AS description FROM dbo.routine AS routine WHERE (routine.routineld “ eroutineld OR eroutineld IS NULL) AND (routine.name LIKE gname) AND (routine.description LIKE (description) ORDER BY routine.name RETURN Хотя сама хранимая процедура довольно элементарна, мы должны обсудить оператор WHERE. Я проверяю все параметры набора, независимо от того, какие параметры посылает пользователь. SQL Server строит свой план работы, учитывая тот факт, что некоторое значение может выть пропущено. Но как ои ато выпмяяегТ Дм таблиц средних размеров чрезвычайно быстро. Однако это не оптимальное решение проблемы. Чтобы показать это, мы возьмем фактический запрос из хранимой процедуры и выполним его, окружив операторами GETDATE () и включив SET STATISTICS 10. Это позволит нам непосредственно проверить выполнение кода. Обратите внимание, что это — лишь один из вариантов проверки работы вашего кода. Посмотреть на план запроса в ОиегрЛв^рхег — также очень хороший способ понять, что npoucxaSum. Мне кажется, что легче отслеживать аргумент, наблюдая количество времени, и сколько требуется операций ввода-вывода, чтобы выполнить оператор.
Кроме определения времени выполнения кода, мы будем намерять: □ количество просмотров (Scan count) — число необходимых просмотров таблицы; □ число логических чтений (Logical reads) — число страниц, читаемых из кэша данных; □ число физических чтений (Physical reads) — число страниц, которые читались е физического диска; □ число упреждающих чтений (Read-ahead reads) — число страниц, помещенных в кэш. Объединение логического чтения и физического чтения вместе ~~ хороший показатель выполнения запроса. Для запросов, имекнцих больший размер, и долее занятых систем логическое чтение может стать физическим чтением при увеличении содержимого каша. SEI STATISTICS 10 OK овсе (reeproccache -- owtc-тка кзша, чтсвы позволить выбрать лучавЛ план SELECT GETDATEO AS before - - запуск варианта процедуры EXEC routinaSlist groutineld ~ 84 = SELECT GETDATEO AS middle — затем запускаем ее снова как немедленный запрос SELECT routine.r&utineld AS routineld, routine.name AS name, routine.description AS description FROM dbo.routine AS routine WHERE (routine.routineld - 84B OR 848 IS NOLL} AKD (routine.name LIKE ' %') AND (routine.description LIKE ' VI ORDER BY routine.name SELECT GETDATEO AS after Этот код дает следующие результаты (я не показал фактические результаты запроса для экономии места): before 2001-01-17 20:41:16.300 Table ’routine'. Scan count 1, logical reads 12, physical reads 0, read-ahead reads 0. (Таблица ‘routine. Количество просмотров — 1, число логических чтений — 12, числ физических чтений — О, число упреждающих чтений — 0.) middle 2001-01-17 20:41:16.330
Table 'routine'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0. (Таблица 'routine . Количество просмотров — 1, число логических чтений — 2, число физических чтений — 0, числа упреждающих чтений — б.) after 2001-01-17 20:41:16.330 Это сообщает нам, что выполивнявоператора и восстановление данным занимают 0.03 секунды для хранимой процедуры и меньше 0.03 секунды для случая иерегламентирусмого запроса (точность типа данных da’tettae •.— 0.03 секунды). В общем, небольшое разли чие. Но будет ли это так всегда? Нет, как мы увидим, выполняя тот же самый тест, фильтруя-столбец description (описание) (который не индексировал): SET STATISTICS IG Ш DBCC treeproccache — очистка кэша, чвовы ноаволивь выОрьтг лучший идеи SELECT GETВАТF. I AS before EXEC routineSliat fdescription - '’процедура в ВД master с именем sp_tielpsql %' SELECT GilDATEп AS middle SELECT routine.routineld AS routineld, routine.name AS ми, routine.eespriptiao AS description FROM dbo.routine AS routine.. WHERE (routine.routine’d - KULL OR SULL IS TOLL) AND (routine, nerte LIKE ' V } AND (routine.description LIKE 'процедура в ВД master с именек ephelpsel *' ORDER BY routine.name SELECT GETDATEO AS after Удалив фактические строки, возвращаемые в виде результатов, получим следующее: before 2001-01-17 21:13:48.550 Table 'routine'. Scan, count 1, logical reads 2022, physical reads 0, read-ahead reads 0. (Таблица 'routine'. Количество просмотров — 1, число логических чтений — 2022, число физических чтений — О, число упреждающих чтений — 0.) middle 2001-01-17 21:13:49.620 Table 'routine*. Scan count 1. logical reads 19, physical reads 0, read-ahead reads 0. (Таблица ’routine. Количество просмотров — 1, число логических чтений — 19, число физических чтений — 0, число упреждающих чтений — 0.)
after 2001-01-17 21:13:49.640 Мы видим, что здесь нет большого различия между временами, требуемыми для выполнения теста — 0.0Т по сравнению с 0.02 сеиувда!. Но на сек раз храшплая процедура требует в десятки раз болшив логических чтетий. Так как логические чтения осуществляются непосредственно из пямятаг, это не дает почти никакого различия саже на моей медленной шшию. Имеется тара альтернатив хранимой процедуры. Мы могли бы включить: □ Использование в хранимой процедуре параметра WITH RECOMPILE. Эго вызывает формирование новой схемы всякий раз норда выполняется запрос. Формирование схемы требует времени, так как плохая схема приведет к реальным ироблемам функционирования; вообще-то это неплохая идея. Вели процедура не вызывается слишком часто, стоимость компиляции обычно весьма мала по сравнению с выгодой из-за лучшей схемы. О Построение вложения IF THEN с любой возможной комбинацией параметров, которую пользователь может послать, и обычно создание запроса для каждой возможности. Здесь главная проблема в том, что имеется (п! + 1) возможных комбинаций параметров. □ Выбор отдельных случаев и построение дополнительных хранимых процедур, чтобы работать а самыми медленными из них. В нашем предыдущем случае с использованием описания, вели это выполнение неприемлемо, могла бы быть создана вторая хранимая процедура, что было бы быстрее. Тогда перед оператором SELECT мы для данного случая переходим на другую хранимую процедуру. □ Продолжение использования хранимой процедуры, но с помощью динамического запроса. Это решение будет выполняться лучше всего, но когда мы выполняем код в хранимой процедуре, то теряем выгоды безопасности хранимых процедур, поскольку пользователь в этом случае должен иметь право управлять данамическим запросом. Давайте продемонстрируем динамическую альтернативу запроса. Здесь мы будем создавать текст в переменной тина varchar и затем будем запускать запрос, используя ЕХЕС: CREATE PROCEDURE routineSlist ( eroutineld int - NULL, ' — перяичньй клвч для определения — единственной строки впагае varchar (60) ’ V , — так же, как > routine. name gdescription varchar(100) •> ' %' — так же, как в routine,description J А 5 — так как сообщения о сумма будут непонятны для клиентов SET NQCOUNT ON — создание переменной для размещения основного запроса DECIARE gquery varchar(80001 — выделение всех полей таблицы для просмотра SET Bmiery - 'SELECT routine. routineld AS routineld, routine .name AS name, routine.description AS description FROM dbo.routine AS routine' S04
— создание переменной для размещения операторов WHERE, крворне - - Оудем создавать для конкретных условий DECIARE gwhere varchar (80001 SET gwhere - ’’ — tat: как НПЫ, + 'что-то' - NULL — добавление имени для поиска к оператору WHERE IF впалге О ' V AND блате IS HOT NULL : SELECT gwhere - gwhere + CASE WHEN LEN(RTRIM(fwhere)) > 0 THEN 'AND ' ELSE ’ * END * ' name LIKE + Впал» + "" — добавление имени для поиска к-оператору WHERE IF edescription <> ' %' AND «description IS NOT NULL SELECT «where - ««here + CASE WHEN LEN(RTRIM(gwhere)) > 0 THEN ' AND ' BLSE '' END + ' паве LIKE ''' + Sdescription + '‘' — выделение всех полей таблицы для просмотра IF groutineld IS NOT NULL SELECT Swhere -gwhere + CASE WHEN LEN(RTRIM(«where)) > 0 THEN ' AND ' ELSE " END + ’ routineld = ' + CAST(groutlneld AS varchar(10}I -- создание переменной для оператора WHERE DECLARE SorderBy varchar(8000} — задание порядке по именам воэвсаиаемо: строк SET gorderBy = ' ORDER BY routine.name' EXEC («query + fwhexe r gordexsy) GO Эта процедура будет работать оптимально для любого набора параметров, передаваемых в нее, но для такого простого запроса она представляет довольно большой тяжело поддерживаемый код, Учтем факт, что некоторые основные хранимые процедуры для восстановления набора строк могут иметь двадцать и более строк SQL-кода с весьма небольшим количеством соединений. Тогда вы начнете понимать, почему динамическое создание запроса в хранимой процедуре (или даже поступающего от клиента) — такая сложная вещь для реализации. По моему опыту процедура оптимизируется, если она работает в пределах спецификаций н не сталкивается с другими процессами. Я предпочитаю создавать код ВД, являющийся столь же общим, как в примере со списком процедур, и оптимизировать его, если необходимо. Мне редко нужно будет оптимизировать этот специфический запрос, но в тех случаях, где он работает слишком медленно или заставляет другие процессы ожидать, одна из перечисленных альтернатив будет всегда решать проблему, В слвдуюлщх трех раздевая мы рассмотрим хранимые пропелуры, которые создают, изменяют и удаляют данные. В та время как подучение данных вызывает трудности, эти типы модификации данных — в значительной степени белее легкая задача. Хотя пользователи будут часто требовать просмотра до тысячи строк данных, они редко’ будут желать корректировать тысячу строк одновременно. Это, конечно, бывает, и мы . можем при необходимости закодировать такие операторы, но модификация даиных обычно осуществляется с помощью первичного ключа. Почти каждое формирование OLTP, с которым я был связан в течение ряда лет, выполнялось в следующем порядке: О находим строку из списка^
□ просматриваем детали строки; □ корректируем строку, удаляем строку или добавляем новую строку; □ просматриваем строку после выполнения действий; □ помещаем строку в список. В атом разделе мы рассмотрим создание основных хранимых процедур, которые создают, изменяют и уничтожают отдельные строки. Можно будет легко расширить примеры для изменения многих строк, просто повторяя части кода. Создание строк Хранимая процедура, использующая оператор CREATE, имеет вид: CREATE PROCEDURE routineSline ( «name varchar(384}, edescription varchar(5C0), enew_rouitneld int - BULL OUTPUT ) AS — объявление переменных, исп-оиьэуемлх для управления оамбками в блоках BECLARE ijrowcount int, Berror int, — содержит число строк, веззрадаемых вызовом ЯИЦ -- используется для размещении кода ошибки — поме вызова ЯНД @msg varchar(255), — используется для предаарительисго — форматирования ооовдцеиий об ошибках gretval int, — обаяя переменная для ноептаневленич -- эеззрапаемых значений от выводов == хряниихх ароцвдур StranName вувпаие,_ выдТад varchar (255) — используется для размещения имени транзакции — используется для размещении тега сообщения об сшибке — задание тега сссвшения об ошибке SET BmsgTag - + objeet_nante (Mpr-aoldi * ';t®e»F' — код управления ошибкой по умрлчанию, испольэуеимй для бдаьцрдаства. проиелур — настраиваемое миге зз.-•••! ииеяи транзакции гарантирует уникальнее имя транзакции = - мн если процедура вызывается аважш в цепочке, так как если одна активна, -- а другая вызывается, то она не может быть на там же уровне вложенности SET atranName = obcect_nane(igproeldj л САвТ(ввпезС1*Уе1 AS varchar(21) -- заметим, что включение этой транзакции не является принципиально -- нваОхолимше, так как — включать ее лишь дая — изменения коде. мы имеем только один оператор. Я лввли того, чтобы предусмотреть воэвожнссть BEGIN TRANSACTION ““ начал© транзакции SAVE TRAHSACTIOH etranNaaie — точка сохранения транзакции, куда мс — межей откатить наше действие вез — ликвидации всей транзакции — выполнение добавления INSERT INTO routine inane, description)
VALUES (бпаиа, Sdeec-ription) — проверка ошибки . - IF (Меиог !- 0) BESIK . . -- фсршфса&мие дополнияельн-ой оисбюг, чтобы яемвш видввь, аде лроивошла SELECT вмад ~ 'Есть проблема при добавлении новой строки в ' •* 'таблицу routine.' + msgTag + *, call» (оператор INSERT>>' RA1SERR0R 50000 Йтзд ROLLBACK TRANSACTION ttcanHame COMMIT TRANSACTIOH RETURN -100 END — затем восстановление автоинкременемвй величины из строки, — которая вызвала ошибку SET finew_rouitneld* - 3co©e_ideatity(l- COMMIT TRAMSACTIW GO Этот код возвращает два элемента. Первый — результирующий набор новой записи: name Test description Тест Далее — набор трех разных сообщений: Server: Msg 2827, Level 14, State 2, Procedure routmeSins, Line 29 Violation of UNIQUE KEY constraint ’AKroutine name'. Cannot insert duplicate key in object 'routine'. (Сервер,* сообщение 2527, уровень 14, состояние 2, процедура rauiinetin», строка 29 Нарушение ограничения UHIQUE КЕТ 'AKmutine_name. Нелняя поместить повторны^ ключ в объект 'routine,)
Server. Msg 50000, Level 16, State 1, Procedure routineStns, Une 37 There was a problem inserting a new row into the routine table. <roiitine$ins:type=P;ca!i=(INSERT routines (Сервер; сообщение 50000, уровень 18, состояние 1, процедура routinetins, строка 37 Есть проблема при добавлении навой строки в таблицу routine.<routlneSins; тип ~ процедура; вызов — (оператор INSERT)>) The statement has been terminated. (Оператор удален.) Наши клиенты должны будут перевести первое сообщение во что-то, что может прочитать пользователь. Второе сообщение говорит нам, какая процедура была вызвана, и какие предпринимались действия, так что мы можем выполнить отладку. Однако обратите внимание, что там, где с процедурой отбора произошла неприятность при создании подходящего оператора WHERE для оптимизатора, мы собираемся определить й фиксированное число- столбцов для добавления информации, что имеет довольно раздражающий побочный эффект, не позволяющий использовать значения по умолчанию, которые были определены для наших столбцов. Это обычно не является проблемой, так как когда требуется взаимодейегаие пользователя, любые значения ги> умолчанию, которые - пользователь мог бы выбирать, будут показаны в пользовательском интерфейсе, и любые, которые он не может изменить, на должны быть частью оператора INSERT. Корректировка строк Корректировка или обновление данных в таблице — снова довольно простая задача: CREATE PROCEDURE routinejupd ( ekey_routinald int, -- столбец мяч*, который w испольвувм как — ключ для восстановления. Обратите внимание, — что Ю1 не можем обновлять первичный ключ gname varchar(3841, fideacription varchar(500), fts_ti»estamp tineatattp — оптимистическая блокировка ) AS — объявление переменных, используемых для управления ошибками в блоках DECLARE (Jrowcount int, — число строк, возврашаеьмя вызовом НМД Serror int, — код оиибки после вызова ЯВД fcnsg varchar(255), — предварительное форматирование сообщений об ошибках Sretval int, — общая переменная для возвраиае»шх значений — от вызовов xpamaet процедур вогапКаие sysname, — размещение- имени транзакции ImsgTag varchar(255) -- разыеожмиа тега со-эбиения об ошибке -- задание тега сеобвения об ошибке SET вшадТад = '<* i- objeet_name (igprccld) + ';type=₽* + keyvalues' * 'gkey_routlneld:' + CONVERT(varchar(10}, Skey_routlnela)
SET BtranName - cbject_nane (ввргосхй) + CAST (Mnestlevel AS varehax (2}j — удостоверьтесь, что пользователь ввел значение tiBt»s.taa^r — поскольку будет очень расточительно, если его не использовать if ets tireestamp is null BEGIN SET tttsg = "Значение time st arap не должно равняться HULL' » (msgTag + ' RAISERROR 50000 (msg RETURN -100 END BEGIN TRANSACTION TRANSACTION itranHame UPDATE routine SET name — Snare, description » ^description WHERE routineld = 0key_routineld and timestamp « ets_timestamp — получение числа строк и уровня ошибки ДЛЯ кода управления овЛкой SELECT Srowcount — ввгоисоипс, йеггог = ввеггог IF веггог 1-й —- еяиЛка возникла ва предела»» этой првивдуры- : BEGIN SELECT @itsg — ’’Проблема возникла при корр-коисозке записи в таблице routine.' ♦ 6~ = g"aq + ' j са!1= (оператор ЦРВИЕ|>' RAISERROR 50000 ®msg ROLLBACK TRANSACTION gtranNare COMMIT TRANSACTION RETURN -100 ran ELSE IF erowcount о 1 — это ве должно выть первичньа» «сличен -- или вались не существует - BEGIN IF (erwcount - 0) BESTS —> если запись сущесЕвуев вея timestamp, — она должна выть модифицирована друтии пользователем IF EXISTS (SELECT * FROM routine . WHERE, routineld “ ikey_routlneld) _ BEGIN SELECT Irasg “ 'Запись таблицы routine модифицирована' + ' другим полгз;а57мем.' EHD ELSE BEGIN SELECT (msg ~ "Запись программы не сужествует." END ' ' END ELSE BEGIN SELECT Stasg - ’ Слишком много записей вето модифицировано - ' SELECT 8шэя = 8msg - (msgTag + 'call-(оператор UPDATE)>' Продолженги койа на смЯующеа, странице
I RAISERROR 50000 gmeg ROLLBACK TRANSACTION StranMame COMMIT TRANSACTION RETURN -100 END COMMIT TRAN RETORN 0 Я не буду демонстрировать этот код; вы должны будете запустить его из примера на www.wrox.com. Обратите внимание иа использование столбца timestamp качестве оптимистической блокировки и на то, как мы с ним работаем: □ Мы пробуем выполнить обновление, используя первичный ключ и столбец типа timestamp. □ Далее, если не возникло никаких ошибок, мы проверяем, сколько строк изменено: □ Если изменена одна строка, все хорошо. □ Если изменено больше одной строки, то изменено слишком много строк. Эта проверка могла бы быть сплошным сумасшествием, вл вто — минимальная стоимость за предотвращение неправильной работы. Важно удостовериться, что фактически происходит точно то, что вы ожидаете получить.- В данном случае только одна строка должна выть обновлена, так что мы выполняем проверку, давы удостовериться, что именно это произошло. □ Если никакие строки не были обновлены, мы проверяем, существует ли в таблице строка со значением первичного ключа. Лиш это так, то строка существует, но она должна быть изменена Иначе она никогда первоначально не существовала. Мы проверяем каждый из этих случаев, потому что это предохраняет от появления дарацких ошибок. Много раз я видел запросы, написанные для модификадаи строк, и вместо передачи реальных значении они передают нули. Пять минут использования Profiler (наиболее важное средство для проверки кода ВД) — и проблемы найдены. Добавление полной обработки ошибок к процедуре предотвращает любую ситуацию, которая может произойти по нашей вине, о очень небольшими затратами. Обратите внимание, что мы снова должны обновлять все столбцы сразу. Было бы очень неэффективно писать хранимую процедуру, которая выполняет обновление, используя только передаваемый столбец. Одаако следует выполнить одну вещь — рассмотреть все столбцы, проверка которых чрезвычайно дорога (или в некоторых случаях нарушает работу программы). Мы можем выбрать два пути: □ Разбить оператор на два обновления. Одно корректирует дорогостоящее иоле (поля), а другое корректирует другие поля. Этот вариант подходит, если ш имеете несколько полей, а дорогостоящие поля редко обновляются. □ Более разумный путь (если имеется только одно или два поля) проверить, изменились ли значения, и затем решить, действительно ли их обновлять. Создаются два (или больше) возможных оператора обновления, которые модернизируют рассматриваемые поля.
Давайте рассмотрим второйиуть. Возьмем случай, когда изменение поля description (описание) нашей таблицы вызывает все виды дорогостоящих проверок в денормалнзаций, требующих более секунды времени, но только изменение значения столбца description занимает 10 процентов времени. Мы могли заменить: UPDATE routine SET name - gname, description = gdescrlptien WHERE routineld — ekey_r<jut£neld AMD timestamp = @ts__timestamp — получение числа строк и уровня оиивки для кода управления ошибкой SELECT erowcount - ggrowcount, gerror = g'gerror на следующий код: — удаляем timestamp иэ зтсгс впвравора, поскольку де не хоттш — обновлять поле description при изменении timestamp IF EXISTS (SELECT * FROM routine NLEi-i routinaid - ekey_routineld AND description « ^description) BEGIN UPDATE routine SET name - Iname WHERE routineld - gkey^routineld AND timestamp - 8ts_timestainp — получение числа с-роя и уровне ошибки для хода управления ошибкой SELECT growcount ~ ggrowcount, terror - ggerrcr END ELSE BEGIN UPDATE roweount SET name =• gname, description “ ^description: WHERE routineld « 8key_routineld AND timestamp - gts_time3tamp -- получение числа строк и уровня ошибки для кеда управления оамбхой SELECT erowcount - ggrc-wcount, gerror - Merror END Заметьте, что мы не изменяем значение и все еще проверяем те же самые моменты в коде обработки ошибки, Кажется, что процедура будет теперь выполняться медленнее, так как имеется большее количество кода, и будет теперь работать медленнее, когда изменяется значение description. Однако, и целом, полное время, затраченное на все вызовы, будет значительно меньше, так как столбец description будет провериться значительно рюкэ, и мы не будем нести затраты, когда он не изменяется. Удаление строк Удаление, или хранимая процедура DELETE, которую мы демонстрируем адась, очень й#®ожа на процедуру обновления за исключением того, конечно, что строка перестает существовать. i ALTER PROCEDURE rout1neSdel eicey_routineld int, gts_ti«fistamp Ыимбмар - KILL -- оптимистическая блокировка ; Продолжение «оде на следующей странице
-- объявление переменных, исиольаушвх для упражнения сшибками ж блоках РЕСIАВЕ Brcwcount int, — число строк, воаврадаеигх вызовом ЧКД 0error int, — код ошибки после вызола ЯИЛ втзд varchar (255J, — предэаонгвлььое форматирование сообщений об ошибв gretval int, — общая переменная для возаращаешгх значенда — ст вьэсзса хранимых процедур OtranName зуэпате, -- размещение имеяи транэакш®! gmsgTag varchar (255) размещение "егэ сообшениж об ошибке — задание тега сообщения ов овпЛке SET fmsgTag • ' ' object_naiBe teiprocld) + ’ : type«P’ + keyvalue»' ' gkey_routinelds’ т CONVERT(varchar(10), fkey_routin»Id) SET gtranName » object_rtame (ввргосХФ + CAST(etnestlevel AS varchar (2}) BEGIN TRANSACTION SAVE TRANSACTION ttranNaee DELETE routine WHERE routineid « gkey_toiitineld AND 8ts_timstamp - timestamp — получение числа строк и уровня ошибки для кода управления ошийкой SE-LECT growcount - Mrcwcount, (’error - tisxvar IF terror !- 0 -- сшибка возникла за пределами этой процедуры BEGIN SELECT Smsg - 'Ttpa&aevsA возникла при удалении записи в таблице routine,' SasgTag + call-(оператор DELETE) >* RAISERROR 50000 0msg ROLLBACK TRANSACTION StranName COMMIT TRANSACTION RETURN -ICO ERD ELSE IF Browcount >1 — это не должно быть первичном клпчом — или запись не судаеявуег BEGIN SELECT вивд — * Сдавшем много записей нрогражл! выло удалено, ' t SmsgTag + call-(оператор DELE.TE)>' RAISERROR 5G000 intsg ROLLBACK TRANSACTION BtranName COMMIT TRANSACTION RETURN -100 END ELSE It в rowcount - 0 BEGIN IF EXISTS (SELECT * FROM routine WHERE routineid » 6key_tOutineI<i) BEGIN SELECT Smsg - 'Запись прогремим модифицирована' + ’ другим пользователем.' + 'call-(оператор DELETE;>' RAISERROR 50000 @mag
ROLLBACK TRANSACTION etranName COWflT TRANSACTION BETUBM -ХОО END ELSE BEGIN SELECT emsg - 'Запись программы, хоторуя вы уытага"ь уавимъ' + ' не существует.' + IssgTag + call”(оператор DELETE)>' RAISERROR 500В0 fmeg == S системе tat должны решить, действительно mt нужно -- рвааливовать эту ошибку, или мы должны эаверв<ть работу и -- воэвратить отрицательное значение, Если вы пытались .— удалять что-то, а это не существует, - это плохо? END END COMMIT TRANSACT ЮК RETURN 0 Выполнение процедуры DELETE чрезвычайно просто. Мы хотим уничтожить одну строку с помощью первичного ключа, для этого берем ее первичный ключ и удаляем строку. Нет никакой возможности удалить лишь части строки или передать неправильное число значений. Это мои четыре центральных хранимых процедуры. Мы теперь эффективно построили набор методов доступа, которые дают довольно удобный в работе интерфейс для создания приложения. Они также обеспечат ловушки в системе безопасности дая нашего приложения, потому что мы можем соответственно выбрать операции на более даффереицщюваином уровне, чем INSERT, UPDATE, DELETE или SELECT. Очевидао, что мне нужно больше чем эти четыре хранимые процедуры, чтобы строить приложения. но они охватывают 80 процентов требований и позволяют создавать редактируемые приложения в довольно общем плане. Когда интерфейс ВД должен удовлетворять очень похожим проектам, код доступа также может выполняться аналогичным образом. Пакеты SQL*кода Пакет SQL-кода представляет собой один или большее количество SQL-операторов, выполняемых сервером в группе. Мы рассмотрим два различных типа пакетов и варианты работы с ними; □ Пакеты е одним оператором — они содержат только единственный оператор, посылаемый серверу. □ Пакеты из нескольких операторов — используются для выполнения нескольких -операций, когда единственный оператор или даже отдельную хранимую процедуру не удастся легко реализовать. Обратите вниманне, что концепция пакетов, содержащих один ила несколько операторов, не имеет никакого отнопк-пия к SQL Server. Он выполняет весь пакет независимо от того, сколько операторов он содержит й что возвращает. Мы делаем различие, потому что должны по-разному кодировать яти два случая.
Один маленький момент, касающийся этого раздела, — мы не занимались сценариями создания ВД, которые- являются одноразовыми действиями. Напротив, мы обсуждаем, создание приложения, чтобы работать с данными и изменять их. Пакеты с одним оператором Пакет с одшш оператором — обычно оператор ISSEBT, ОPDAIE, DELETE или SELECT, но это может выть и хранимая процедура или системная функция, вроде функций DBCC, Единственный случай, когда мы ие должны рассматривать добавление дополнительного, кода, чтобы выполнить пакет, — это когда мы выполняем единственный оператор. Однако обратите внимание, что мы очень ограничены в возможностях работы с этими операторами. Мы можем послать SQL-оператор любого типа, но не можем делать больше одной корректировки данных. Следовательно, не допускается никакая предварительная проверка или манипуляция данными, и обработка ошибок в пределах оператора запрещена. Например, если мы выполняем следующее: Я UPDATE customer 1 В SET flratName - JЭнда' Ц WHERE customerld - 100 Здесь в таблице custamec (заказчик) обновляется запись е custamerld (идентификатор заказчика!, равным ТОО, — в поле firs Шаше (имя) пс-мещается значение Днди. ... -то не можем считать, что это действие выполнено успешно, так как здесь возможна ошибка. Проверка состояния ошибки и работа с самой ошибкой возлагаются иа конкретную вызывающую программу. Однако имеется и другое беспокойство. Существует ли вообще эта строка? Если нет, ваша команда не будет обновлять никаких строк. Много раз я видал операторы (выполняемые на VMeript в АЗР-етраинце) подавно следующему (VBScr^t — язык сценариев на основе Visual Basic; АВР — Active Server Page — активная страница сервера): I update customer SET firstSame - 'Эти' WHERE customerld ™ 0 который не выполнялся, потому что пользователь сделал орфографическую ошибку в имени, забыл включить Option Bxplicit (явное описание ошибки), но включил Оа Error Reeume Next (в случае ошибки переключиться иа следующий оператор). Что более неприятно, если Вы не завываете использовать ti®estamp, или выбираете любую оптимистическую блокировку для вашей ВД, всякий раз, когда вы изменяете строку, оптимистическая блокировка бесполезна. Мой совет = если вы собираетесь использовать пакеты некомпилируемого кода, чтобы модифицировать данные, вам следует создать общий объект для управления корректировками. Удостоверьтесь также, что вы получили правильный объект, потому что опыт подсказывает мне, что имеется много способов сделать глупую ошибку, которую очень трудно определить в вашем кода. Пакеты с несколькими операторами При обсуждении пакетов с несколькими операторами мы будем в основном следовать той же самой структуре, которую использовали в хранимых процедурах. Однако мы должны рассмотреть две темы, которые нуждаются и другой обработке в данном контексте:
□ транзакции; □ обработка ошибок, Транзакции Как мы отметили в разделе Транзакции в начале главы, очень важно, что когда мы преобразовываем данные в SQL Server, то должны удостовериться, что никогда не начинаем транзакцию программного обеспечения нашего клиента без того, чтобы закрыть ее в том же самом пакете. Единственная проблема здесь связана а реализацией, потому что каждый раз, когда мы используем оператор SO, Query Analyser начинает новый пакет. Обработка ошибок Когда мы объединяем вместе несколько операторов, то должны рассмотреть, что делать в случае отказа. Мы имеем оператор RETURN при кодировании хранимых процедур, но RETURN не работает в пакете. Когда мы решаем, что заканчиваем пакет, и желаем завершить, один из способов состоит в том, чтобы задать переменную и использовать ее для условного выполнения блоков кода. Предположим, что мы имеем операторы, которые хотим выполнить, и должны удостовериться, что каждый из них выполняется до окончаиия транзакции. Мы помещаем их в транзакцию, но что, если одни из них терпит неудачу? BEGIN TRANSACTION <оператор_1> <оператор_2> <ойератор_3> COMMIT TRANSACTTON Мы можем написать код вокруг этих операторов DECLARE SerrorNb* int, greturnValue int, SerrorOut bit SET Berroruut = 1 BEGIN TRANSACTION — содгртхт номера, оиивож после — кажжог-о оператор* - — содержи* воаарашммо» виачвмив от — хранимой иронедуры -- Слаг, сензбаввимй, выполнять иди нет -- следующие операторы <опврвтор_1> SET gerrerNbr - fferror . IF евгхогЯЬг О 0 OR greturnValae < 0 BEGIN RAISBRROR 50000 'Ошибка в выиолнвюп» <anepaccp_l> ’ SET веггог-Оис «0 END IF SerrorOut - 1 BEGIN «оператор 2> SET gerporNbr • ввеггог IF SerrorNbr <> 0 OR SreturnValue < 0 Продолжение кода на следующей странице
BEGIN RAISERROR 50000 'Ошибка » выполнении <оператор_2>' SET BerrorOut - О END EKD IF gerrorOut “ 1 BEGIN <оператор_3> SET Berrortibr « fgerror IF gerrorUbr <> 0 OR SreturrtValue < 0 BEGIN RAISERROR 50000 'Ошибка в выполнении <оператор_3>' SET gerrorOut - 0 END END IF gerrorOut <> 0 BEGIN RAISERROR 50000 'Ошибка а выполнении пакета' ROLLBACK TRANSACTION END ELSE COMMIT TRANSACTION Снова не забудыпе, что если вы получили ошибку в триегере, пакет с трудом остановится, и вея ваша внешняя обработка ошибки никогда не будет вызвана. Кодирование же доступа таким образом в любом случае — хорошая идея, потому что никакие ошибки ограничений не будут разрушать пакет. Всякий раз, когда вы хотите послать от клиента несколько- операторов, чтобы все их выполнить или ие выполнить ни одном, вы должны использовать схему, подобную этой. Обратите внимание, что я включил большое количество, по-видимому> избыточных смещений об ошибках для целей отладки. Очевидно, сообщение относительно первой ошибки, которая вынуждает пакет выполняться не так, как надо, наиболее важно передать пользователю, переведя его в удобочитаемую для него форму. Обратите внимание на различную закраску отдельных частей этого пакета. Вели вы просто объединяете вместе набор операторов, возможно, без наборов формируемых результатов, нет никакой проблемы в построении механизма пакета, чтобы добавить набор операторов и затем построить сценарий этого. Вы могли бы также добавить немного описательного текста к сообщению об ошибке так, чтобы что-то наподобие "Ошибка в выполнении <оператор_1 >" стало бы более информативным относительно используемых строк: "Ошибка при удалении заказчика с cuetomerld (идентификатором! — 1234". Когда мм приступаем к выполнению этого пакета, добавляются закрашенные части пакета и обработчики ошибок после каждого оператора, Конечно, это не единственный способ строить обработчик ошибок, Я часто использую другую технику. Вместо использования: IF gerrorOut = 1 при построении пакетов я использую GOTO в обработчике событий:
<onep*sop_2> SET веггохКЬх = Няхзсос IF lezrorNbr <> 0 OR IrecurnValue < 0 BEGIN RAISBRROR 50000 'Ошибка в выполнении <оператор_2>' SOTO errorOut EJJD и завершающую часть пакета в виде COMMIT TRANSACTION GOTO endOfBetCh errorOut: RAISERROR 50000 'Свивка в выполнении пакета' ROLLBACK TRANSACTION endOfBatch; Это приемлемое решение, ио оно использует неприятный оператор GOTO. Основное назначение этого метода — возможность просто добавить код ошибки в конец каждого оператора, но вообще-то это связано с моей ленью. Наиболее важный момент заключается в том, что мы держим наш пакет в безопасном состоянии и должны быть уверены. что транзакция закрыта. Если она не закрывается. могут произойти дне действительно причудливые вещи: □ Каждый раз, когда вы изменяете данные, они будут надежно заблокированы, возможно, блокируя других пользователей после завершения этой транзакции. Q Так как ваше приложение ве знает, что »то оно подаесило транзакцию, любые другие команды, которые вы посылаете, будут также заиушаться внутри агой же самой транзакции. Как упомянуто в Обработке ошибок в раздала Хранимые процедуры. нужно выполнить завершение всех пакетов, посланных серверу, или полностью отменить их. Если ошибка происходит в середине пакета, пакет ожидает что-либо, чтобы обработать ошибку перед продолжением. Компилируемый SQL-код по сравнению с нерегламентированным SQL-кодом при создании приложений При создании OLTP-приложений мы должны осуществить овределеяный выбор. Должны мы или нет создавать хранимые процедуры? И если мы их создаем, то должны требовать, чтобы весь доступ к ВД выполнялся через них? В атом разделе мы исследуем различные аргументы и дадим рекомендации по лучшему способу создания приложений. Нерегламентированный SQL-код Нерегл-аментировакный SQL-код иногда называется прямым SQL-кодом, поскольку к этому коду ничего не добавляется. "Нормальный" путь, которым большинство средств общается с SQL Server — использование нерегяоментированкых пакетов SQL-кода. Как мы видит, такой код очень полезен, когда происходит объединение в пакет, и иногда это единственный путь, которым мы можем строить сценарии для одноразовых изменений д анных. Но что можно - сказать, если мы строим пользовательский интерфейс для нашей OLTP-СУВД?
Использование некомпилируемого керегламентмрованный SQL-кода имеет некоторые преимущества по сравнению с формированием компилируемых хранимых процедур: □ большая гибкость; □ более быстрая разработка; □ лучшее функционирование в некоторых ситуациях. Большая гибкость Поскольку мы создаем его в самую последнюю минуту, нерегламентироаанный BQL-коя не зависит от некоторых основных требований к хранимым процедурам. Например, пусть мы хотим создать пользовательский интерфейс для списка заказчиков. Мы имеем десять столбцов, которые могут быть добавлены к оператору SELECT, входящие в таблицы, которые перечислены в операторе FROM, Очень просто включить в пользовательский интерфейс список столбцов, который пользователь может брать, чтобы настроить свой собственный список. Тогда программа может формировать список только с теми столбцами в списке SELECT, которые нужны пользователю. Так как некоторые столбцы могут быть очень большими, ио содержать весьма немного данных, лучше возвратить несколько столбцов вместо всех десяти. Например, предположим, что мы имеем првдстмлениеу_си sterner (представление-заказчик) со столбцами customerld (идентификатор заказчика), name (имя), number (номер), of£i-eAddress (адрес офиса) с каждой строкой адреса, отделенной слэшем, phoneNumber (номер телефона), lastSaleDate (последняя дата продажи) и yearToDateSales (продажи за гой» Нам, конечно, ие потребуется показывать пользователям customerld, хотя он всегда нам будет нужен. Если пользователь осуществляет продажу, то он мог бы сформировать совокупность параметров, содержащую name, number, lastSaleDate, lastContaetDate (повяедаяя дата контакта) и yearToDateSales. Интерфейс пользователя послал бы следующий запрос серверу, чтобы получить требуемые строки: SELECT customerld, паша, number, lastSaleDate, yearToDateSales Ц from v_custcaeer Другому пользователю просто потребуется посмотреть имя и номер, тогда мы можем выполнить следующее: I SELECT customerld, пале, number FROM v_customer Это сохранит нам ширину полосы при возврате результата пользователю. Но мы все еще вычисляли вы yearToDateSalee, lastCcntactDate и lastSaleDate. Как мы видели, в хорошо разработанной ВД эти значения, вероятно, будут рассчитываться, а не храниться, что приведи к тому, что этот запрос Судет содержать массу ненужных данных. В этом случае мы можем развить запрос на две возможности. Вели пользователь запрашивает столбец о продажах, то клиенту будет послан полный запрос: I SELECT customer.customerld, customer.name, customer.number , sales.yearToDateSales, sales.lastSaleDate FROM customer LEFT OUTER JOIH (SELECT customerld.
ISUM (itessCowt * prtceFaid) AS yearToDateSales, MAX(date) AS lastSaleDate FROM sales GROUP SY custojaerldj AS sales ON customer. ~тсжг!я =» sales.customerld Если пользователь не запрашивает столбец о продажах, клиент пошлет.. - И SELECT customer.customerld, customer.name, customer.number — обратите вюшаиие на эапяите в начале строки. Это позволяет легче — прокош«ечтир©»ать своябць* строки или программно отдавить им. в — реальном приявжении , sales.yearTaDnteSales, sales.IsstSaleBate Ц FROM customer . LEFT OUTER JOIN (SELECT cwtomerld, - - - SUM(it«nCount * pricePaid) AS year”oDateSales, MAX A.3 lastSaleOate FROM sales GROUP ВУ cuetoeerld) AS sales ON customer,customerld - sales,customerld Таким образом, мы получаем гибкость выполнять только те, что действительно нужно, То же самое можно сказать с-б операторах. INSERT и UPDATE, Одна из проблем, которую мы выявили, когда создавали эти два типа хранимых процедур, —.это то, что очень трудао изменить столбцы в списках INSERT в UPDATE. Это будет тривиально (е точки зрения SQL) при построении наших приложений, лишь аключлв столбцы, которые должны измениться в списке столбцов. Например, возьмем из вышеупомянутого поля таблицы customer (заказчик) — customerld, name и number, В нашей хранимой процедуре оператор UPDATE всегда должен посылать все столбцы: UPDATE customer SET name » впаяе, number gnwnber WHERE customerld ~ Scustomerld. Но что, если изменяется только столбец name? И что, если столбец number — часть индекса, и он имеет проверки данных, которые требуют для выполнения три секунды? Используя прямой SQL-код, мы просто выполняем: UPDATE customer SET name « ic3ra= , number = Rnwuber | WHERE customerld - ^customerId Более быстрая разработка Скорость разработки, на мой взгляд, — неприятный мометт и зависит1 исключительно от персонала. В учреждениях, где я работал, был случай, когда один хороший SQLr-программиет мог не отставать от пяти или около того программистов пользовательских интерфейсов, строящих приложения, а используя генераторы кода, мы могли вы увеличить это до десяти или иятнадпатя. Однако здесь имеются две препятствия, Во-первых, хороших SQL-программистов трудно дайтя-и, во-вторых, многие менеджеры не считают нужным иметь програХ5МИСТОВ, связанных только е SQL-программированием»
Таким образом, если вы не имеете специализированного специалиста, пишущего запросы, разработка кода пользовательского интерфейсе с помощью пакетов SQL-запросов может Выть одним иа способов решения задачи. Однако стоит упомянуть, что главные прорехи возникнут, если общие правила (сохранять транзакции в одном пакете и стараться сделать один пакет совпадающим е одной транзакцией} не выполняются. Лучшее функционирование Выгода в функционировании от использования прямых SQL-запросов в основном связана с моментами, которые мы рассмотрели в предыдущем разделе, посвященном гибкости. Поскольку мы можем опустить части запроса, которые не имеют смысла в некоторых случаях, мы не попадаем в ситуации, когда выполняем ненужный код. SQL SERVER 7.0 и 2000 хранят планы этих запросов, ио следует иметь в виду, что посылаемые операторы должны быть идентичны, кроме, возможно, скалярных значений аргументов поиска. "Идентичные" означает нх действительную идентичность; добавление комментариев или даже символов пробела нарушают план. SQL Server иожет строить планы запросов, которые имеют параметры, — которые позволяют повторное использование плана последующими запросами — но, по всем статьям, хранимые процедуры лучше, когда следует использовать хранимые планы для реализации. Компилируемый SQL-код Компилируемый ЭДЬ-код делится на два типа объектов. Один тип — хранимые процедуры (и функции, компилируемые внутри иих), Другой тип — трштеры; мы не будем здесь рассматривать триггеры, поскольку предварительно установили, что они полезны только в очень определенных обстоятельствах. В атом разделе мы рассмотрим четыре специфических свойства хранимых процедур, которые делают их замечательными средствами создания приложений; Q они ограничивают доступ к данным; □ функционирование; □ инкапсуляция; □ безопасность. Ограниченный доступ к данным В то время как хранимая процедура может исполяять мноижратвые действия, она выглядит для пользователя как один вызов, Как было рассмотрено ранее в разделе Хранимые процедуры, мы можем строить почти объектно-ориентируемые методы для наших объектов ВД, так что пользователю никогда ие понадобится выполнять вызовы нереыаментированных запросов. Это дает нам очень большие организационные преимущества. Если мы ограничим пользователей обращением только с хранимыми процедурами, существует конечный набор путей, которыми пользователи могут обращаться к данным. Следовательно, если мы имеем проблему в функционировании, то можем оптимизировать хранимую процедуру довольно простым способом- (во многом подобно тому, как мы делали выше в разделе ИнкапсуляцшЦ вместо необходимости прочесывать строки и строки кода, чтобы найти проблему. Функционирование Хранимые процедуры могут дать нам лучшее функционирование, размещая план оптимизированного запроса при первом выполнении процедуры. Последующие запросы используют точно тот же план, что и первый. Со всеми типами соединений, возможными таблицами, индексами, расширениями текста представления и т, д. оптимизация запроса —>
нетривиальная задача, которая может потребовать всего несколько миллисекунд. Теперь, вероятно, при создании однопользовательского приложения, вы сможете сказать, кто интересуется этим запросом, но когда число ваших пользователей увеличивается, это начинает усложняться. Как мы только что обсудили в разделе Нерееламентированный SQL-код. Лучшее функционирование, SQL Server мажет хранить планы запросов SQL, и хотя при этом имеется несколько строгих правил, управляющих их формированием, на самом деле могут выть различия. Однако при доступе с помощью хранимых процедур правила гораздо мевее строгие. Каждый раз, когда вы вызываете хранимую процедуру с любым набором параметров, она использует один и тот же план (если только мы не вызываем ее, используя WITH RECOMPILE). Это сохранит вам миллисекунды, что является важным, если имеется большое количество пользователей вашей системы, В то время как SQL Server обеспечивает довольно хорошую работу, кэшируя нерееяаментированные SQL-запросы, прекрасно, когда он кэширует сами запросы хранимой процедуры. Нужно понята, что хранимые процедуры оптимизируются на основе начальных параметров, которые передаются ей. Это иногда вызывает проблемы, если вы запускаете одну и ту же- процедуру е двумя равличными наборами параметров. Например, мы можем иметь таблицу с неклаетерным индексом в поле, где 60 процентов элементов таблицы имеют одинаковые значения, в то время как все другие значения в -таблице уникальны. Если .бы ваш поиск был связан с неуникальными значениями, шпингк не был бы лучшим способом поиска строк (так как издержки выборки при обращении через индекс к страницам данных будут, вероятно, больше, чем просто рассмотрение всех строк данных иепосредствеиво), Если же поиск осуществляется по уникальным значениям, лучше использовать индане» Какое бы значение ни использовалось первым, оно становится планом, который используется всеми последующими запросами. Имеются два способа работать в такой ситуации, если это становится проблемой: □ Добавить условие WITH RECOMPILE в объявлении хранимой процедуры, вызывая каждый раз удаление плана. О Создать процедуру, которая вызывает две других процедуры, одну .— для уникальных случаев и другую — для неуникальных. Это довольно тяжело, но если это — часто используемая хранимая процедура, то может быть лучшим способом. Инкапсуляция Одно действительно большое преимущество использования хранимых нроцедар как средства маскировки между пользователем и таблицами заключается в том, что оии упрощают доступ к данным. Даже простой оператор SELECT, который обращается к единственной таблице, может быть сложно сформулировать и поедать от клиента, хотя он и намного меньше многотабличного запроса или даже запроса с несколькими операторами. Например, рассмотрим следующий оператор к одной таблице: I SELECT pereonld, firetMame, lastMame FROM person WHERE peraonld - 1 Здесь в таблице person (человек) ищутся поля per son Id (идентификатор человека), firstNeMe (имя) и lest-Пат (фамилия) строки, у которой person— 1.
В символическом коде мы можем получить следующее: Ц query = "SELECT personld, firstMane, lastMame FROM perec-n WHERE personld « 1" Мы могли бы выполнить его, используя ADO-технологию, или какой-нибудь другой набор объектов, который позволяет клиентам использовать данные из SQL Server. Если же мы выполняем хранимую процедуру, то можем просто сделать следующее: DIM query AS STRING f query - 'EXECUTE personSget Spersonld 1" Мы можем создать хранимую процедуру на Сервере следующим образом: CREATE PROCEDURE personSget ( Spersonld int ) AS SELECT persanla, firetNaae, Xaetttaee FROM person WHBRB personld - fperaonle Первый момент, который вы, вероятно, заметили — это то, что хранимая процедура потребовала больше усилий для своего создания» Однако это очень простой пример. При рассмотрении в моем коде более сложных операторов SELECT, состоящих, по крайней мере, из пяти или шести строк, а иногда и до пятнадцати или двадцати, гораздо более легко для клиентов использовать код в виде единственной строки текста. Учтем также то, что в то время как наши примеры содержали запросы е единственным оператором, ранее мы рассмотрели запросы с несколькими операторами. Кода мы должны выполнить в процедуре более одного оператора, будь то заполнение временной таблицы или, возможно, даже обновление нескольких таблиц, мы должны использовать гораздо более сложный синтаксис формирования пакета, который был рассмотреи в разделеЛГакеты SQL-кода. Попытка реализовать сценарий программным путем и затем выполнить его, — довольно грязный путь. Маскирующий слой между пользователе» и таблицами в виде хранимых процедур дает также преимущества оптимизации. Как мы обсуждали в разделе Хранимые процедуры, поскольку детали замаскированы и поэтому скрыты от пользователя данных, мы имеем конечный набор запросов для оптимизации; когда пользователь говорит, что данная процедура медленна, мы можем это легко установить. В то время как совершенная оптимизация может быть более трудна при использовании хранимых процедур, сами они позволяют закодировать повторное использование. Безопасность Мы можем ограничить доступ пользователей к конкретным хранимым процедурам и, следовательно, к конкретным основным таблицам. Результатом является то, что, используя хранимые процедуры, легко формировать надлежащий набор прав, который ограничивает пользователя возможностью видеть только то, что мы хотим, чтобы он видел.
Советы Каждый из этих двух вариантов SQL-кода имеет "за" и "против”. С одной стороны, мы имеем игрсмажсктпированньш SQL-код. Ои очень гибок — требует ивболщого времени разработки, но приведи к некоторым связанным с ним небольшим всплескам загрузки во время функционирования. Воя эта гибкость может также создать хаос при попытке выполнить настройку приложения. С другой стороны, используя хранимые процедуры для доступа, мы экономим неиоторовлремя компиляции.вря многократных использованиях, и имеем преимущества маскировки структуры ВД от фактического польаоигеаия -данных, в сущности, помещая защитный уровень доступа к ВД между пользователем и самими данными. Все рассмотренное говорит, что лучшим выбором для большинства OLTP-ВД будет построение их, используя доступ с помощью хранимых процедур. Вы могли бы предположить, что основная причина этого — функционирование. Использование хранимых процедур улучшает функционирование, особенно, когда вы имеете чрезвычайно занятую БД. Однако, как только фирма Microsoft добавила планы запросов, хранимых для прямых SQL-запросов, эти хранимые виды запросов, которые использует OLTP-ВД, — обычно не такая уж большая проблема, тис что они устранили много недостатке»-при работе с этими объектами. Таким образом, самый большой выигрыш от использования хранимых процедур — инкапсуляция. Поддержание маскирующего слоя между приложеяйямй и структурами данных — неоценимое средство для создания надежных приложений и сокращения затрат на обслуживание. Ключ к этому прост. Как только пользователи поработают с приложением некоторое время, изменения неизбежны. С хранимыми процедурами мы можем включить эти изменения в таблицы и при этом не нужно изменять наши приложения.-Хранимые процедуры также делают наш и приложения ище, забирая большую часть обработки ошибок ЯМ Д-операций из кода представления. Когда мы посылаем пакеты кода к SQL Server, то овычн-о так или иначе посылаем определение хранимой процедуры, так почему бы не строить SQL-запрое однажды и обычно его и использовать? Рассмотрение безопасности Обеспечение безо паси ости — другая обязанность администратора ВД, которую мы должны понять достаточно глубоко, чтобы могли создавать соответствующие приложения, которые защищают- не- столько от неправильных данных, сколько от неправильного использования данных. Как приложения, обеспечивающие доступ к данным, будут использовать безопасность, — это то, что мы детально и последовательно изложим. Мы обсудим, как спроектировать план безопасности дои нашей ВД, и рассмотрим, как можно использовать безопасность SQL Server в нашем собственном коде. Мы отойдем от любого технического обсуждения безопасности — типа того, использовать ли доверительные соединения (Windows Authentication) или стандартную безопасность SQL Server — в значительной степени потому, что с точки зрения проекта это нас действительно не болнует. Внутри ВД безопасность будет означать то же самое.
Используя безопасность SQL Server, мы будем строить план безопасности, который предотвратит любое нежелательное иепользманне наших объектов любым пользователем, будь то член нашей организации или хакер. Будучи архитектором данных, чрезвычайно важно тщательно проектировать наши потребности безопасности я терминах того, кто чте может делать и с чем (как мы начали поступать е нашими действиями ранее в главе 5), Наша цель состоит в том, чтобы позволить пользователям выполнять любые задачи, которые ни нужны, но запретить решение любых других задач илж просмотр любых данных, которые им запрещены. Формирование ролей и связанных е ними пользователей довольно легко выполнить с помощь» Enterprise Manager, и мы не будем зге рассматривать. В SQL Server мы можем управлять правами доступа почти к каждому типу объекта. Следующая таблица показывает виды операций, которые нас будут интересовать, для каждого типа объекта е точки зрения безопасности наших ВД: Объект Права Table (таблица) SELECT, INSERT, UPDATE, DELETE View (представление) SELECT, INSERT, _’?1ч7Е, DELETE Column (столбец) — в таблицах и представлениях SELECT, INSERT, UPDATE, "DELETE Function (функция) EXECUTE- Stored Procedure (хранимая процедура) EXECUTE Существуют также права на References (ссылки) к таблицам, которые не подходят к этому обсуждению. Далее мы можем разрешить или запретить использование этих объектов нашим пользователям. Мы, конечно, ие собираемся предоставлять доступ каждому пользователю индивидуально, поскольку мы можем иметь множество пользователей в наших ВД. Повтому SQL Server обеспечивает нас понятием ради (ранее известное как группы в SQL Server 6.Х и предыдущих), которой мы можем предоставить права доступа, к данным и затем связать ее с пользователями. Каждый пользователь в ВД является членом, по крайней мере, общедоступной роли, но может быть членом нескольких ролей. В принципе роли могут быть членами других ролей, Возьмем, например, любую типичную систему учета кадров, которая содержит информацию о служащем, наподобие имени, адреса, должности, хозяина, вида оплаты и т. д, Нам будут, вероятно, нужны разные роли, например: □ Менеджеры отдела кадров (HRManagers) — может выполнять любую задачу в системе. □ Сотрудники отдела кадров (HR Workers) — могут обслуживать любой атрибут в системе, но требуется саньщионировдние записей, чтобы изменить информацию о жаловании, □ Менеджеры (Managers) — вте менеджеры в Компании будут в агой роля, но они могут видеть только информацию о своих собственных рабочих. □ Служащие (Employee») — могут видать только свею собственную информацию, н могут изменять только свою собственную информацию об адресе.
Каждой иа ролей затем предоставляется доступ ко воем ресурсам, в которых они нуждаютея. Член роли менеджеров будет, вероятно, также и членом роли служащих. Тогда, как сказано выше, они могут видеть информацию о своих служащих, а также ж свою. Имеются три различных команды, которые SQL Server использует для добавления или удаления прав в каждой из наших ролей: □ GRANT — дает права использования объекта. □ DENY — запрещает доступ к объекту независимо от того, предоставили ли пользователю права доступа по любой другой роли. □ REVOKE используется, чтобы удалять любой оператор GRANT или DEMY, который был выполнен. Ведет себя подобно отмене разрешения. Почти каждый оператор разрешения, который вы будете выполнять, будет оператором GRANT. Как правило, мы просто даем разрешения роли, чтобы выполнить задачи, которые являются характерными для нее, Команда т~ч Y должна использоваться только тян, как большинство команд типа ляп ре та. когда независимо от того, сколько до «того вы предоставили прав доступа к объекту, пользователь не будет иметь доступа к нему, если имеется лишь одна команда DENY. В качестве примера возьмем систему людских ресурсов, которую мы только что обсуждали. Мы не отменили бы доступ к роли служащих, так как менеджеры также являются и служащими и должны иметь права на т. Как только мы создали все наши объекты — включая таблицы, хранимые процедуры, представления и т, д, — можно предоставлять права на них. Мм уже упомянули в . специальных разделах, посвященных объектам, что при наличии хранимых процедур, функций и представлений не нужно предоставлять права иа основные объекты, которые используются в этих процедурах, функциях и представлениях. Это справедливо в том случае, когда пользователь, создающий объект, является также и пользователем, владеющим этим объектом. Так как мы будем создавать нашв объекты как владельцы ВД, обычно это не является проблемой, Одна дополнительная проблема, на которую мы должны обратить внимание. — это то, что каждая БД имеет специальный набор встроенных ролей: □ db_accessadmin — пользователи, относящиеся к этой роли, могут добавлять или удалять пользователей БД; □ db_backupoperator — пользователи, относящиеся к этой рсмш, могут поддерживать ВД; □ db_datareader — пользователям, связанным с этой ролью, позволено читать любые данные в любой таблице; □ db_datawriter — пользователям, связанным с этой ролью, позволено записывать любые данные в любую таблицу; □ db__ddladmin — пользователи, связанные с этой ролью, могут добавить, изменить или удалить любые объекты в БД (другими словами, выполнять любые ЯОД-операторы); □ db_denydatareader — пользователи, еммшвые е мой релью, не могут видать . никакие данные в ВД, хотя они все еще »гут видать данные черед хранимые процедуры; □ db_denydatawri ter — во многом напоминает роль db_denydatareader; пользователи, связанные с агой ролью, не могут изменять никакие данные в БД, хотя они все еще могут изменять данные через хранимые процедуры;
О db_cwneг — пользователи, ввязанные с этой ролью, могут выполнять любые операции в БД; □ db_aecurityadain — пользователи, связанные с этой ролью, могут модифицировать и изменять допуск и роли в ВД, Решение, делать ли ваших пользователей членами какой-либо из этих ролей, полностью зависит от ситуации. Я люблю создавать роль пользавателя-адаинистратора — пользователя, кто может добавлять пользователей к ВД и связывать их с ролями. Создаете приложения, которое отображает эти значения — довольно легкая задета е исподазованием SQL-DMO (SQL Data Management Objects — объекты управления данными SQL), Тогда, как только пользователь выл создан администратором ВД, пользователь в роли администратора может связать их с его ролями ВД. Программно мы можем определить некоторую основную информацию относительно безопасности пользователя в ВД: □ IS_MEMBER(<роль>) —сообщает кам, является ли текущий пользоаатель членом данной роли; □ trSER — сообщает нам имя текущего пользователя; О PERMISSIONS (OBJECT_ID(' <;'Мя_объекта>' } > — позволяет нам видеть, имеет ли пользователь права выполнить некоторое действие. Например, мы могли бы построить.’Запрос, чтобы определять все соответствующие хранимые процедуры, которые пользователь может выполнять: I SELECT specif1а_палв, CASE SHEN VEUtlSSiatS (OBJgCT_ID(specific=ftam)) 4 32 - 32 THEN 1 ELSE 0 END AS canExecute ГНОИ infoonat:ion_schewa.routines WHERE specific_name LIKE 'routine.5V Когда m выполним его, то получим следующий список specific_name canExecute routineSdel 1 rotitneSins 1 routineSiist 1 routineSupd 1 Если вы ограничили доступ к основным таблицам, то уже создали каждый элемент безопасности в вашей системе, с которыми пользователь ии программист должен будет когда-либо иметь дело. Например, возьмем четыре процедуры, которые мы перечислили выше. Ограничивая доступ к процедуре routineSiist, пользовательский интерфейс может просмотреть это» список доступа и определить это, если потребуется когда-либо выполнить данную процедуру. То же самое верно для процедур добавления, обновления и удаления. Если пользовательский интерфейс не может использовать процедуру rovtinefdel, он даже ие будет показывать Delete (удаление) в- своих меню.
PERMISSIONS () = довольно универсальная функция, которая может использоваться, чтобы определить, может ли пользователь выполнять большинство операций в SQL Server (чтобы получить дополнительную информацию, проконсультируйтесь в SQL Server Books Online). При создании приложений мы будем пробовать все лучшее, чтобы использовать возможности SQL Server для наших нужд безопасности — это поможет нам избежать когда-либо формирования таблиц пользователей и их прав, В остальной части этого раздела мы расширим концепцию использования безопасности SQL Server, чтобы иметь дело с некоторыми методами, которые должны будем ияогда использовать. Мы рассмотрим: 3 безопасность на уровне строк; О безопасность на уровне столбцов. Безопасность на уровне строк Обычно это относится к ситуации, когда мы хотим дать пользователю права доступа к таблице и всем столбцам, но не хотим, чтобы они могли видеть все строки, учитывая некоторые критерии. Безопасность на уровне строи было очень трудно осуществить в предыдущих версиях SQL Server. В SQL Server 2000 мы можем выбрать довольно простое решение этой проблемы, используя определяемые пользователем функции. Для нашего первого примера возьмем таблицы artist (артист) и album (альбом) из предыдущей главы. Мы собираемся рассмотреть разрешение пользователям видеть только альбомы Beatles (Битлз), если они являются членами роли Beat lesFan. Предположив, что мы имеем созданную роль, сформируем следующую определяемую пользователем функцию: CREATE VIEW v_album AS SELECT aLbumld, паяв, artistld FROM dbo.album H WHERE artistld <> 1 — e Битлз В OR is_reembar (' BeatlesFan' ) “ 1 GO Затем мы выделим отсюда: I SELECT * FROM dec. valb'jr Этот запрос возвратит: albumld name artistld 3 quadrophonia 2 9 tommy 2 потому что я не член роли BeatlesFan. Это, конечно, не очень хорошо. Что, если мы хотим запретить пользователям просматривать работу других артистов? Нам нужно расширить пример. Можно было бы создать таблицу с гругшой отношений artist (артист), которая здесь названа rlsAlbum (rls означает row-level security — безопасность на уровне строк), 18- 1868 527
I CREATE TABLE rlsAlbura ( rlsAlbunld int HOT HOLL IDENTITY, artistld int, яеиЬегОСйгоирЫаие вуямте ) Здесь создается таблица rlsAlbwa (альбом с безопасностью на уровне строк/, содержащая три поля: автоинкрементное rlsAibumld (идентификатор альбома с безопасностью на уровне строк) типа int, не может принимать значение ПУЩ artistld (идентификатор артиста) типа int и memberOfGrcupName (имя члена группы) типа sysname. Затем мы создадим представление, которое коррелирует с безопасностью в таблице artist: — мы строим представление, которое включает налу таблицу - - везопасноети на уровне строк: ALTER VIEW valbum AS SELECT album,albumld, album,name, album.artistld FROM dbo.album AS album JOIN (SELECT DISTINCT artistld FROM rlsAlbum WHERE IS—MEMBER (memberOfGroupHatBe} - 1 AS security CN album.artistld - security.artistld GO Затем мы выделим столбцы из представления л о добавления каких-либо грунт: — выполнение как членов никаких групп; SELECT * FROM v_album и мы не получаем строк: albumld name artistld Далее мы можем добавить некоторые группы к таблице rlsAlhua, чтобы позволить е помощью db_owner и равных логических ролей видеть значения в таблице: — параметр db_owner, вероятно, добавлен s триггер INSERT для таблицы album INSERT INTO rlsAlbum (artistld, memberOfGroupNaBiel VALDES 11, 'db_owne г') INSERT INTO rlsAlbum (artistld, BiemberOfGroupName) VALUES (2, ’ db_ owner') INSERT INTO rlsAlbum(artistld, memberOfGroupName) values (1, 'BeatlesFan*) INSERT INTO rlsAlbum (artistld, memberOfGroupWame) VALUES (2, 'WhcFan'J GO Когда мы будем выделять строки из таблицы, то получим:
albumld name artistld 1 the white album 1 2 revolver 1 4 abbey road 1 8 rubber soil 1 10 yellow submarine 1 11 please please me 1 9 tommy 2 3 quadrophonia 2 Теперь, если пользователи ие имеют прав на основную таблицу, они не смогут видеть какие-либо строки, если вы не дали для представления конкретные права роли, членами которой они являются. Безопасность науровне столбцов Безопасность SQL Server имеет средства для разрешения и отмены нрав на конкретный столбец, но они тяжелы для использования. Они также не будут работать, когда мы используем представления и хранимые процедуры для доступа к нашим данным, так как объекты используют безопасность своего владельца. Если мы хотим использовать хранимые процедуры для доступа ко веем нашим данным (что я предпочитаю), нужно рассмотреть различные возможности. В этом разделе мы дадим возможную альтернативу создания нескольких поеяставлевий или процедур, чтобы вертикально делить наши данные. Возможный способ реализовать это состоит в использовании технологии, напоминающей пример уровня строк, чтобы закрыть столбец, который пользователь не имеет право видеть. Используя разрешения SQL Server, мы можем расширить наше предыдущее представление, чтобы возвратить значения только для столбцов, к которым пользователи имеют доступ: ALTER VIEW v_album AS SELECT album.alBuald, — предполагаем, что пользователь может впаять — -з-~ель иервичнс-го ключа ICASE ИНЕЯ TERMISSIONS(OBJECT_ID('album’) , 'name') 6 1-1 THEN album.name ELSE NULL END AS name, CASE WHEN PERMISSIONS(OBJECT_ID('album'), 'artistld') 61-1 THEN album.artistld ELSE NULL END AS artistld FROM db©,album AS album JOIN (SELECT DISTINCT artistld FROM rlsAlbum WHERE IS__MEMBER (memberOfGxaupHante} - 1) AS security ON album.artistld - security.artistld Обратите внимание, что мы здесь возвращаем NULL как индикатор, потому что столбец не допускает значение KULL, В некоторых случаях, когда, например, NULL является фактически допустимым значением для столбца, мм могли бы добавить дополнительный столбец, возможно, кое-что наподобие canSeeNameFl (флаг возможности видеть имя) типа bit, который сообщает приложению, может ли пользователь видеть столбец.
Это может привести к небольшому количеству издержек, но при этом можно обеспечить почти любую безопасность на уровне столбцов ж строк. Важный аспект этого решения — то, что мы можем теперь использовать это представление в хранимой процедуре, и независимо от того, кто является владельцем хранимой процедуры, мы способны ограничить использование столбца в довольно общем случае, который ищюльаует только везопасность SQL Server, Рассмотрение взаимодействия между БД До сих пор все, что мы обсуждали относительно кода и проблем, касалось локальных ВД. Вообще говоря, кроме факта, что данные не в пределах той же самой логической единицы функционирования, имеется небольшое различие доступе к данным вне границ местной БД. Это обычно делает работу с данными тяжелее, тем более что ограничения внешнего ключа не могут использоваться для управления потребностями ссылочной целостности. Кроме того, так как другая ВД будет поддерживаться и использоваться по-другому, мы теряем часть защиты, которую имеем в мире отдельных ВД. Хотя доступ к данным во внешних БД создает трудности, иногда ато неизбежно. Типичным примером могла бы быть попытка связать имеющуюся в наличии систему с вашей доморощенной системой. Может оказаться легче организовать доступ к таблицам из различных серверов, чем попытаться включить их в вашу ВД. В этом разделе мы определим некоторые из проблем, которые возникают при работе с другими ВД. Разобьем все случаи иа два варианта: □ тот же самый сервер; □ другой сервер. Тот же самый сервер Доступ к данным в различных ВД, но на одном и том же сервере во многом подобен обычной (та же самая ВД, тот же самый сервер) ситуации. Однако имеется несколько проблем, связанных с обозначением, безопасностью и корректировкой данных, которые вам нужно исследовать. Обозначение при использовании того же самого сервера Это довольно просто. Чтобы иметь доступ за пределами нашей ВД, мы просто включаем ВД в обозначение объекта, к которому обращаемся. При этом используется следующее соглашение: Ц <имя_ВД>.«владелец».<имя_ов'ьек'та> Это подходит для таблиц, хранимых процедур, функций в т, д. Безопасность на том же самом сервере Каждая ВД имеет уиикальиое значение db_owner (владелец ВД). Следовательно, один db_owner не может создать процедуру для доступа к данным в другой БД без того, чтобы не получить разрешение от db_owner этой ВД.
Если мы на сервере имеем две ВД, в настоящее время нет никакого способа коррелировать этих двух владельцев а позволять им передавать права на объекты через хранимую процедуру. Например, рассмотрим следующую процедуру: I CREATE PROCEDURE procedure^testCrossDatabase AS SELECT field! FROM database^.dbo.table GO Пользователь будет нуждаться не только в правах доступа к процедуре procedure$testCrossDatabase, но и к ВД databaseZ, я мы будем должны предоставить ему возможность явного доступа к ВД и объекту. Плохо также, что это касается и триггеров. Так как пользователь даже не будет, вероятно, и видеть данные, которые триггер коитролирует, кажется опасно кре доставлять ему волше права на таблицу в другой ВД, только для того, чтобы проверить значение. По этим причинам было Вы выгодно копировать данные, которые нужно контролировать, в вашу собственную БД, конечно же, в зависимости от фактического размера этих данных. Это защитит ваши данные от просмотра, но, конечно, обойдется дороже, чем выполнение взаимного поиска в ВД. Корректировка данных на том же самой сервера В первой части этой главы мы рассмотрели, как SQL Server использует транзакции и блокировки, чтобы обеспечить ситуацию, когда наши операции или полностью заканчиваются, или полностью не выполняются, и, что более важно, без прерывании от других процессов. Нет никакого различия в кодировании, когда мы обеспечиваем доступ к данным в различных ВД. Мы просто используем тот же самый синтаксис и обращаемся к данным в обеих ВД. Те же самые механизмы транзакций беспрепятстзенно работают в любом случае. Например, пусть мы имеем старую собственную заявочную систему и новую закупочную систему, которая была создана другой компанией. Когда мы создавали первую систему, то включили таблицу, которая содержала список товаров, которые покупатели метут приобрести, и которая показывала, откуда товары могут быть поставлены. Теперь, когда мы имеем новое третье лицо и закупочной системе, то хотим использовать ее функциональные возможности заказа товаров. Мы хотели бы автоматически создать- заказ товаров, когда к заказам имеет доступ человек с полномочиями. Так, хранимой процедуре raquisition$f inalize вызов INSERT INTO для вышеупомянутого еииека будет ааменен на INSERT INTO ДЛЯ таблицы зека» товаров: I CREATE PROCEDURE «quisleion$finalize ... вспоногательный кол удален для ясности .,. BEGIN TRANSACTION INSERT INTO dbo. regulsitienStatus (requiaitionNumber, statusldl VALUES (IrequisitxonNuwber, 0statusId) — обравэтка с-ибск INSERT INTO purchasing.dbo.purchaseorder(nueber, date) Продолжение кода на следующей странице
I VALUES tgpurchaseOxderMumbex, getdate(») -- обработке ошибок — и также поместить товары COMMIT TRANSACTION Поскольку эти две БД находятся иа одном и том же сервере, оиерация кажется пользователю неуязвимой. Однако так как теперь имеется несколько протоколов транзакции в смеси, SQL Server не может выполнять простую транзакцию. Он должен использовать очень важную концепцию, называемую завершением с двумя стадиями. Завершение с двумя стадиями Завершение е двумя стадиями представляет совой протокол, который позволяет серверу связываться с ВД, которой требуется выполнить задачу как транзакцию в двух различных логических областях. Тот же самый протокол используется и когда СОМ+ использует объекты СОМ для выполнения задач, не относящихся к ВД, в пределах транзакции. Используя СОМ+, мы можем смешивать транзакции объектов БД с объектами файловой системы, используя завершение с двумя стадиями, чтобы в значительной степени гарантировать целостность транзакции’ (имеется некоторая возможность неудачного завершения транзакции на одном источнике прост» из-за времени ожидания, но такая возможность очень маленькая}. Имейте в виду, что диспетчер начинает транзакции так же, как это обычно делается. Транзакция начинается на каждой отдельной БД или ресурсе точно так же, как эго выло бы, если это была единственная транзакция. Затем, когда мы посылаем завершение, чтобы перевести счетчик транзакций в нуль, начинается завершение с двумя стадиями, □ Стадая подготовки — Посылает предварительное сообщение каждому из ресурсов и сообщает ему о завершении транзакции. В этом случае ресурс заботится обо всех протоколах и делает все необходимое, чтобы оставить действие транзакции неизменным. По завершению ресурс сообщает об этом инициатору транзакции, и сообщает также, успешно ли все выло выполнено. Q Стадия завершения ~ Как только SQL Server получает сообщения от ресурсов об успешном завершении, каждому ресурсу передается заключительное сообщение о завершении. Если любой иа ресурсов объявляет о неудаче, то вся транзакция откатывается. Во время стадии завершении, если команда получена только одним иа ресурсов диспетчера, который потерпел неудачу, ваша работа может нарушиться. Нет никакой возможности бороться с этим. Однако это чрезвычайно маловероятно, поскольку эти команды идут действительно быстро и в течение стадии завершения не должно выть никакой возможности возникновения блокировки — мы уже должны заблокировать элементы, и все, что осталось, — послать заключительную команду завершения. Различные серверы (распределенные запросы) Я только хочу сделать краткое упоминание о распределенных запросах и представить функции, которые могут использоваться, чтобы установить отношения между двумя SQL Server или SQL Server и источником данных OLE DB или ODBC. Имеются два метода, которые можно использовать:
□ Связанные серверы Мы можем строить связь между двумя серверами, регистрируя имя "server", которое нам затем позволит организовать доступ через имя, состоящее из четырех частей (<имя_сервера>. <ВД>. <владелеи>.<таблииа>), или через интерфейс орелдаегу (открыть запрос). □ Нерегламентироваяные связи — Используя функции орел rowset (открыть набор строк) или opendatasource (открыть источник данных), мы можем получать таблицу данных из любого источника OLE DB. Так же как наши транзакции могут охватывать несколько БД, мы можем также иметь транзакции, которые охватывают различные серверы, использующие Т-SQL. Если необходимо, мы можем обновить данные в нашем распределенном источнике данных с помощью сервиса DTC, который является частью SQL Server и ООМ+. Мы используем следующую команду: BEGIN DISTRIBUTED TRANSACTION UPDATE extemalS^rrer .database .owner.table СОИМХТ TRANSACTION Можно написать наш код почти точно так же, как будто все это было на том же самом сервере. Обратите внимание, что, если бы мы написали BEGIN TRMSACTIOti вместо BEGIN distributed TRANSACTION, транзакция была бы локальная для этого сервера, и операции, которые касаются других серверов, не будут частые транзакции. Мы не будем входить в детали этих команд, поскольку они могут быть весьма сложвыми и иметь много различных настроек для многих разных ситуаций. Я редко использовал их при создании OLTP-систем и предпочитаю, когда это возможно, импортировать/дублировать все данные в один и тот же SQL Server — главным образом, с целью увеличения скорости. Связанные серверы часто используются при установлении тиражирования отношений. Для получения большего количества информации относительно распределенных запросов и тиражирования см. SQL SERVER 2М0 Books Online или ”Professional SQL Server 2000 Programming" Роба Виейра (Wrox Press, ISB.V 1-861004-48-6). Учебный пример Мы теперь готовы начать строить доступ к структурам ВД, которые создавали в прошлых двух главах. Для згой части учебного примера мы возьмем три конкретные задачи. Эти задачи позволят другим программистам, работающим с нашей БД, начать, писать конкретный код для использования структур: □ Создание основных хранимых процедур корректировки (добавление. оЛовлеиио, удаление), которые иногда называются CRUD-процедурами (Create, Read, Update, Delete — создание, чтение, обновление, удаление), а также процедур распечатки я заполнения домеяов для наших таблиц доменов. □ Построение специальных хранимых процедур, которые поддерживают важные задачи нашей системы и которые отмечены в диаграмме действий. □ Определение набора групи безопасности и определение, какие объекты они должны будут, вероятно, использовать иа самом высоком уровне.
Основные хранимые процедуры Возвращаясь к нашей диаграмме иа главы 10, мы создадим еледаопрге четыре хранимые процедуры для четырех различных таблиц: □ transactloaSins □ bankSupd □ payeeSdel □ accountSliat Мы создадим также процедуру transactionlypeldcmainFill. Слова обратите внимание, что этот код может выть считан с www.wrox.com. Процедура размещения сделки Это, по существу, хранимая процедура размещения, которую мы разработали ранее в этой главе с небольшими дополнительными комментариями. В этой процедуре мы вставляем в таблицу каждое иоле и включаем обработку ошибок, чтобы выдать дополнительную информацию, если возникает ошибка. Как обычно, мы должны помешать в скобки имя таблицы transaction (сделка), так как это зарезервированное слово. CREATE procedure transactionsins gaccountld int, «'лзж varchar (201, edate smaildatetime, edescription varchar (1000b 8amount money, , Bsignature varchar (iGOb Bpayeeld int, etransactionTypeld int, 9new transactionId int - NUbb OUTPUT содержит новое яиачвиив ключа для автоинкрементного первичного ключа AS BEGIN — параметры обработки оиивок DECIARE 0me<g varchar(255), — сообщение об оиивкв 0ms<gTag varchar (2551, — тег сосбивния об оивбкв gtranNatne sysname — имя точки сохранения - - зевание тега сообщения ов ошибке и тег= “анаскшва — inestlevel - уровень вложенности содержит уникальное значение — среди визсвов к процедуре, к;» они является вложенншш) SET gmsgTag = ' + objeet_na»»(BeprocIdJ b*type»₽' SET StranSaiee - object_name(etpracld) + CASTfSgaestlevei AS varchar (2) ) BEGIN TRANSACTION SAVE TRANSACTION etrenSame INSERT INTO [transaction](accountld, number, date, description, amount.
signazure, payeeid, transact.i.onTypeld) VALUES(Saccountld, gnumber, gdate, edesatiption, gamount, isignature, gpayeeld, StraneactionTypeldJ — проверка ошибки IF (egerror !• 0) BEGIN — вначале завершение транзакции, чтобы минимизировать каш транзакции, — подвешенной в ожидании завершения сооОшения ROLLBACK TRANSACTION SELECT gmsg - ''Имеется проблемы с добавлением новой отроки в * 4 'таблицу transaction.' + msgTag * */call-(добавление в transaction)>’ RAISERROR 50Q00 fmsg RETURN -100 END — scope_identity ограждает нас от получения начальных одинаковых вначений . BET 9neK_transaccioaId - scope_identityО COMMIT TRANSACTION END Процедура обновления таблицы bank В процедуре обновления мы просто корректируем поле name (название) таблицы bank (банк) передаваемым значением. Обратите внимание на параметр тина timestamp и иа те, как мы испольауем его, чтобы реализовать оптимистическую блокировку для пользователей этой процедуры. CREATE PROCEDURE bankSupd i ekey_bankld int, — ключевой столбец, который гш будем — использовать как ключ для — обновления.Заметьте, что мы ие можем — корректировать первичный ключ Snaiw varchar (384), .Jts_timestamp tiatestaisp — оптимистическая блокировка ) AS -- объявление переменных, используемых для обработки ошибок в блоках DECIARE Srowcount int, — число сорок, возвращаемый вызовом явд вегtor int, — код ошибки после вывоза ЯМД 8msg varcher (255), -- сообщение об сшибке gretval int, — возвращаемое значение от вызова —> хранимой процедуры 9tranName sysname, -- имя транзакции SmsgTag varchar(2551 — тег сообиения об сшибке -- задание тега сообщения об ошибке SET впьвдТад - + object^nanse (вfprocld) + ’;type“₽' + '/keyvalue”' т '0key_bankld:’ + CONVERT(varchar(101, 8key_bankIcU Проволжвние кода на елеВушцей странице:


ELSE IF Srowcourit >1 — это не должно Выть первичшш ключом — или запись не существует ‘ BEGIN SELECT enisg - 'Слишком много получателей плат-ежеЙ было удалено. ’ >• UnisgTag + call»(удаление иа payee)>' ROLLBACK TRANSACTION StranName COMMIT TRANSACTION RAISERRCR 50000 #msg RETURH -100 END ELSE IF irowcount - 0 BEGIN IF EXISTS [SELECT * FROM payee WHERE payeeld - gkeypayeeld) BEGIN SELECT @meg = 'Запись таблицы payee модифицирована* + ' другим пользователем.* • cal 1= (удаление из рауее)>* ROLLBACK TRASACTION ItranName COMMIT TRANSACTION RAISERROR 50000 gmeq RETURN -100 END ELSE BEGIN SELECT 8meg = 'Запись в тавлипе payee, которую ш пыежвтесь удалить* ' не существует.' + ftnsgTag + '/call-(удаление из payee)>’ RAISERROR 50000 виде — Это зависит от потребностей системы, действительно ли вы - - должны фактически реализовать ату сшивку или «ы должны — здесь завершить и вернуть отрицательное значение. Если вы — пробовали что-то удалить, а оно не существует, - это плохо1 END END COMMIT TRANSACTION RETURN Э Процедура формирования списка счетов Чтобы создать эту процедуру, мы должны включить информацию о счетах и несколько возможных фильтров — в данном случае, number (номер), bankid (идентификатор банке.) и bankllame (название банка). Когда мы видим список счетов, то можем пожелать видеть также и информацию о банке, поэтому ее и добавляем. CREATE PROCEDURE accountSllst i laccountld in- - NULL, Bnuffiber varchar(20) - ' 4*, @bankla int - NULL, SbankNare varchar (20) = ' V первичный ключ, чтобы подучить ксикретиую строку так же, как и у account.name
} AS — тек как известное число сообщений будет проблемой для клиентов SET НОСОВ» ОМ - - значение по умолчанию параметра Snumber - '%', если переданная — величина - MULL IF вй-Uffiber IS BULL SELECT ЙпииЬег - '%' — выделение всех полей (кроме tiaestemp} из таблицы для прооиотра SELECT account,accoentIdr account.bankid, account,number, bank.name AS bankManw FROM dbo.account AS account JOIH dbo.bank AS bank OK account,bankid - bank.bankid WHERE (account.accountld » eaccountld OR gaccountld IS NULL) and (account.number LIKE gnuaberj AND (account,bankid - gbankld Oft gbankld IS NULL) AND (bank.name LIKE BbankName) ORDER BY account.name RETURK & Процедура заполнения типов доменов сделок Процедура заполнения доменов используется для заполнения комбинированного окна типов сделок наподобие формы сделок. Обратите внимание, что мы размещаем название в поле описания даже если в таблице уже имеется поле описания. Это сделано для того, чтобы даже если в таблице нет названия или описания, мы все же возвратили описание, чтобы упростить пользователю кодирование. CREATE PROCEDURE transa.ctionTypeSdomainFill AS BECIH -- асе процедуры заполнения доменов вовврацают одни и те же имена -- долей, так что пользователь может кодировать им единообразно, — используя общую модель SELECT transactionTypeM AS Id, папе AS description FROM transactionlype ORDER BY transaationType.name EHD Специальные хранимые процедуры Мы сосредоточимся на двух из имеющихся действий и рассмотрим, как можно еоадата- храиимые процедуры, чтобы поддержать их. Мы рассмотрим: □ баланс счета; □ получение информации о счете.
Действие по выполнению баланса счета Вернувшись к нашей диаграмме действий в главе 5, мы выбрали дайетвиеВахапсе (баланс) и описали его следующим образом; "Позволяет пользователю урегулировать элементы. которые были сообщены банком". Мы не будем писать никакого конкретного кеда SQL для этого действия, но рассмотрим эту довольно сложную ситуацию и покажем, как мы решим ее в нашей БД. Чтобы выполнить операции баланса, мы должны будем, вероятно, закодировать следующие хранимые процедуры: □ statementSinsert (размещение «чета), statmentltemSinsert (размещение элемента отчета) и, возможно, statementSinsertltems (размещение элемента в отчете). Так как мы можем загрузить записи statementltem (элемент отчета) ив Интернета, можно бы использовать последнюю процедуру, чтобы загрузить значения во временную таблицу и затем вставлять весь набор сразу, Можно добавить триггер INSTEAD OF, чтобы поместить все неправильные строки в таблицу обработки исключений, когда они восстанавливаются из загруженного файла. □ tranaactionSlistPossibleMatchesToStatementltems (список возможных типов элементов отчета) — эта процедура предназначена для добавления несогласованных сделок к stateraentltem и дает все точные сделки, основанные на равенстве [transaction] .number и statement Item, number для чеков и, если возможно, transactionType. Мы должны быть уверены, что объединение дает отношение ' "один к одному" для transaction и statenentltem, □ transaction?reconcile (согласование со сделкой) — это позволит нам согласовывать transaction со statementltem. □ Пакет хранимых процедур, которые должны скоординировать информацию с экрана и используют transactionSlistPossibleMatchesToStatementltems, чтобы выполнить запрос. □ Нам потребовалась бы процедура, наподобие нашей общей процедуры transactionSiist, для конкретного счета с- единственной целью показать несогласованные элементы. □ statementltemSlist (список элементов отчета) —> его дало бы нам список элементов, которые не должны выть связаны с данной сделкой. Эта и предыдущая процедуры позволили бы нам иметь форму соответствия (снова пользовательский интерфейс), которую мы могли бы использовать, чтобы связать элементы. □ statements!inalizeReconciliation (завершение согласования отчета) — она размещала бы запись в таблице AccountReconciliation (согласование счета), чтобы сообщить отчету, что согласование закончено. Нужно бы добавить триггер к таблице отчета, который проверяет, что вс е кредиты и все дебеты (положительные и отрицательные сделки) в таблице transaction соответствуют обшей сумме в отчете. Ужесточение требований к системе могло бы позволить автоматически добавлять изменения сделки в таблицу, чтобы балансировать отчет без каких-либо ошибок.
Имейте в виду, что в последнем пункте я добавил новые требования к системе. Эти функциональные возможности должны рассматриваться, как дополнения, и быть обсуждены с менеджером проекта и пользователем. Это может, быть особенностью, которая клиенту не нужна, или. возможно, изменит еео мнение. В течение процесса реализации системы вы будете думать о новом и охлаждать стремления делать добавления к проекту. Архитектура, которую вы создаете, должна быть достаточно хороша, чтобы поддержать новые особенности, но никогда не добавляйте новые особенности, к изделию, которых нет в спецификациях, без того, чтобы, не получить согласие клиента (и увеличение вашей оплаты). Как вы можете видеть, это весь список процедур, которые нужно написать. В аависииости от штата кодировщиков, следующий шаг должен выл вы, вероятно, дать вереии-ааглушви этих процедур, чтобы кодировщики пользовательского интерфейса могли продолжить свою работу. Мы также обнаружили несколько дополнительных триггеров, которые должны быть написаны, чтобы охватить таблицы statement и accountReconeile. Возникновение дополнительных триггеров и требований не есть что-то невероятное — никакой процесс проектирования не совершенен. Действие по получению информации о счете Мы должны выть способны овеепвчить это действие с помощью довольно простой хранимой процедуры, так что напитом ее здесь. Будем считать, что пользовательский интерфейс выл определен так, чтобы дать выпадающий список счетов и иметь возможность просматривать текущую информацию, включая: О текущий баланс (полный дебет, полный кредит): □ общее количество несогласованных сделок; □ общее количество сделок: □ последняя дата отчета; □ последняя дата согласования. Итак, мы напишем следующую процедуру: CREATE PROCEDURE accountSgetAccountlnformation £ Saccountid int ) AS — Отметин, что так как мы создаем саблииу transaction, используя — зарезервированное слово, мы должны использовать квадратные скобки — для имени » нашем sanpoce SELECT account.accountWumber, statement.date, -- если эта величина. МИЛ, то отчет — никогда ие Выл получен accountReconeile.date, — если эта величина КЩХ, то отчет — никогда не был согласован SUM( [transaction].amount) AS accountBalance, SUM(CASE WHBM [transaction].amount > 0 Продолжение кода на следующей странице
THEM [transaetienj.amount ELSE 0 END) as total.Credits, SOM(CASE WHEN [transaction].amount < 0 THEN [transaction].amount ELSE 0 ENO) AS totalDebits, SUM (CASE WHEN transactionReconcile .transactionReconcileld. IS NOT NULL THEN transactionAadunt ELSE 0 END} AS unreconciledTotal FROM dbo.account AS account —: счет ноже® не икеть сделок LEFT1 OUTER JOIN dbo. (transactionI AS [transaction} — сделке иожет быть- и* согласована LEFT OUTER JOIN dbo.rransactionRecancile AS transactionReconcile ON [transaction].transactionld - transactionReconcile.tranaaction.Jd ON account.accountld - [transaction].accountld -- отчет может быть никогда ие получен для счета LEFT OUTER JOIN dbo.statement AS statement LEFT OUTER JOIN dbo>. actor nr Reconcile AS ac-ceuntReconcile ON statement.statemenild • accountReconcile.statementld ON account.accountld - [transaction}.accountld WHERE accountld = eaccountld GROUP BY account.accountNumber, statement.date, accountReconcile.date Безопасность в учебном примере Наконец, мы должны определить вашу безопасность. Наш пример имеет две роли безопасности, и мы, вероятно, будем желать добавить другую роль для целей администрирования: □ Accountuser (основной пользователь счета) — основанная на диаграмме действий, эта роль будет только способна рассматривать информацию о счете. Конечно, потребуется специальная хранимая процедура acaountSgetAccountlnformation наряду с любыми поддерживающими хранимыми процедурами для построения формы. □ AccountincjClerk (клерк бухгалтерского учета) — как видно на диаграммы, эта роль будет иметь права на любую оиределенную в системе процедуру, □ UserAdministrator (администратор пользователей) — эта роль является членом стандартных ролей БД db_securixyadmin и db_accessadrain. Имеется некоторая двусмысленность в спецификациях, но мы хотим реализовать пользователя, который может просматривать вею информацию о счете —Accountuser. Мы, конечно, должны спросить менеджера проекта и клиента, должны ли некоторые счета быть недоступны некоторым пользователям. Вели это так, то мы могли вы добавить систему безопасности на уровне строк, чтобы реализовать это,
Резюме В этой главе мы рассмотрели многие из наиболее важных проблем, которые должны знать при реализации доступа к данным и корректировках в наших приложениях. Мы обсудили все "за” и "против” использования хранимых процедур, нерегламентироааммых SQL-запросов и представлений. Особое внимание было обращено на то, что можно сделать, чтобы перехватить ошибки — обработка ошибок является одним иа слабых мест SQL Server — и посмотрели иа реализацию безопасности с помощью ролей пользователей и хранимых процедур. Мы детально рассмотрели транзакции и блокировки, временные таблицы, значения NULL и курсоры. Наконец, сформировали некоторые из основных хранимых процедур, требуемых, чтобы реализовать ваш учебный пример ВД. Хотелось вы надеяться, что эта глава ответила на многие из ваших вопросов относительно доступа к данным в их корректировки и улучшила ваше понимание важных проблем. Было бы интересно видеть, как эти идеи затрагивают ваши собственные проекты. В следующей главе мы рассмотрим одну из наиболее трудных задач для проектировщика ВД — перевод требований к системе в фактические требования к аппаратным средствам.
Определение требований к аппаратным средствам Введение Эта глава включает в сева задачи, которые не играют большую роль в проектировании ВД, и поэтому обычно не рассматриваются как сфера деятельности архитектора данных. Одаако теперь, когда физически реализован наш проект, мы имеем некоторые идеи относительно объема данных, аппаратных средств, которые требуются для работы. При рассмотрении этих требований данная глава не представляет ©обой полное руководство по проектированию аппаратных средств, во скорее как учебник для начинающих по таким вопросам, так как мы не всегда будем иметь роскошь в вида штата администраторов БД, который занимается этим. Конфигурация аппаратных средств, на которых запускается SQL Server, может быть как допустимой, таи и недопустимой. Как вы думаете, какие аппаратные средства действительно необходимы вашим приложениям? Как вы думаете, могут ли ваши приложения вырасти до размеров, когда они перегрузят вашу систему? Эта глава даст вам информацию, которая нужна при выборе аппаратных средств, на которых будет размещена БД, Более конкретно, мы посмотрим, скол ько места потребуется вашей БД (тема, известная как объемный анализ), и рассмотрим реализацию вашего сервера. Объемный анализ — процесс, в котором мы исследуем типы размещаемых данных и ожидаемую скорость их роста. Анализ типов БД, с которыми вы столкнетесь, необходим. Следуя этому, мы займемся- формальным анализом таблиц и постараемся упростить его для огромного большинства случаев. Важное значение имеет архивирование старых данных, которое часто увеличивает евободаую область, улучшая функционирование. Реализация сервера включает анализ различных свойств сервера и СУБД,
Типы БД Очевидно, что имеются неисчислимые приложения с множеством ВД, которые поддерживают их, Хотя всегда трудно выделить ясные различные направления, обычно ВД делятся на два направления: OLTP (On-Line Transaction Processing — обработка транзакций в реальном масштабе времени) и OLAP (On-Line Analytical Processing — аналитическая обработка в реальном масштабе времени). Мы поочередно рассмотрим каждое на них. OLTP OLTP БД — обычно высоко нормализованные таблицы, которые увеличиваются с помощью многочисленных коротких транзакций, БД кассовых терминалов или банкоматов (ATM — Automatic Telling Machine) банковских счетов — хорошие представители OLTP-БД. Из-за нормализованного характера таблиц размеры строк и использование индексов обычно минимизируются, Основные таблицы в этих БД обычно будут расти е постоянной скоростью X (вычисляемое значение) строк за заданный период времени. Этот период времени может быть часами или днями, иди, даже, месяцами или годами. Так как вы хотите собрать требования к хранению данных для OLTP-БД, то должны будете измерить ату скорость роста, предсказывая ее настолько точно, насколько воз-.южно. Некоторые OLTP-ВД могут поддерживать больше одного приложения; хорошим примером могут быть приложения бухгалтерского учета, где вы можете иметь отдельную БД для каждой бухгалтерской книги, расчетов кредиторской задолженности и расчетов модулей счетов дебиторов. С другой стороны, по причинам безопасности SQL Server и инфраструктуры таблиц системы, вы будете часто встречать такие приложения, написанные с единственной ВД для всех этих модулей. Таким образам, такая БД бухгалтерского учета, может иметь поднаборы таблиц, которые поддерживают отдельные бухгалтерские книги, расчеты кредиторской задолженности и расчеты модулей счетов дебиторов. По существу, с точки зрения объемного увеличения эти пс-днаборы можно рассматривать как отдельные приложения для таблиц и анализировать их отдельно. OLAP 01 АР-БД используются для подведения итогов и формирования отчетов на основе данных, формируемых OLTP-приложениями и нереляционными источниками. Возьмем, например, компанию страхования, которая собирает данные требонанийв одной из своих О1ТР-ВД. Они могла бы иметь отдельные OLAP-ВД, которые суммирует эти данные с целью определения законности требований и формирования отчего» е анализом деловых решений. Некоторые из них будут вызывать эти БД, являющиеся хранилищами рабочих данных, и резервировать OLAP-аббревиатуру для специализированных авеадиых схем ВД, которые обеспечивают дальнейшую детализацию хранилища рабочих данных. Для наших целей мы будем использовать общую аббревиатуру OLAP, когда будем говорить о формировании отчетов в БД. работающих на вершине механизма SQL Server. Данные обычно загружаются в эти БД с помощью дискретных загрузок пакетов во время наименее занятых периодов. Такое использование прежде всего ограничено тем, что подобные системы используются только для чтения, в то время как таблицы обычно денормализованы и часто будут иметь большие размеры строк и попытке уменьшить число соединений, используемых в запросах. Чтобы ускорить выполнение запросов, каждая таблица в OLAP-БД обычно обладает многими индексами. Эти ВД увеличиваются на основе
ожидаемого роста подпитывающих их исходных БД, Однако индексы также должны всегда рассчитываться. Поскольку возникают новые требования к формированию отчетов, будут добавляться новые таблицы и/или новые индексы, Однако, так же как и в жизни, все не только черное и только белое. В то время как болышянстао ВД обычно будет иметь или тип OLTP, или тин OLAP, некоторые будут поддерживать приложения, которые имеют как OLTP-, так и OLAP-компоненты. ВД заработной платы, например,.обеспечит программа OLTP деловых бухгалтерских записей, связанных с циклическим видом оплаты. В то же самое время, ежекварталяые и ежегодные таблицы отчета OLAP-типа будут часто формироваться из этих данных, чтобы обеспечить государственные налоговые требования. Снова в этом случае OLTP- и ОЬАР-части БД должны быть проанализированы отдельно, Рост OLTP-таблиц Можно ожидать, что не все таблицы в OLTP-ВД будут расти с одной и той же самой скоростью. Некоторые из них используют ежедневные данные (издательство может ожидать получать новые заказы ежедневно), в то время как для других будет ожидаться менее быстрый роет (мы надеемся получать новых заказчиков, но не с той же скоростью, с какой мы получаем заказы). Рассмотрим эти сценарии более подробно. Быстрый рост Обычно большинство OLTP-приложений будет иметь от двух до пяти таблиц, которые содержат основную часть данных всей системы. Размер этих таблиц обычно определяет объем, постоянно увеличивающийся в течение времени. Приложение, связанное с закупками, например, будет обычно иметь таблицы Purchaseorder (заказ закупок) и POLineltem (строка заказа закупки). Для каждого заказа закупок вы будете иметь одну или несколько строк в таблице POLineltem. Во многих приложениях вы могли вы иметь разнообразные самостоятельные части. Например, приложение кадровой системы имело бы набор таблиц, которые, прежде всего, поддерживают сторону приложения, связанную с трудовыми ресурсами, и набор таблиц, касающихся заработной платы. В первом случае таблицы, включающие историю параметров сотрудников, тина обучения, назначений, персональных данных и данных компании охватили вы большую часть данных. Во втором случае таблицы истории, включающие платежи по всему разнообразному списку начисления жалованья, налогов и отчислений от жалованья, внесли бы вклад в основной рост ВД. Медленный рост В дополнение к этим таблицам имеются вторичные таблицы, которые увеличиваются в течение времени, но менее быстро. В приложении, связанном с покупками, это включило бы таблицу доменов, которая охватывает список продавцов, покупателей и допустимых изделий. В приложении, связанном с кадровой системой, его включило бы персонал сотрудников и таблицы общего назначения. Эти таблицы увеличиваются во Времени, хотя в более медленном темпе, чей главные операционные таблицы приложения. Часто эти таблицы относительяо маленькие по сравнению с основными таблицами, и если грубо оценивать, можно их считать постоянными в период жизни приложении.
Отсутствие роста Очевидно, имеются некоторые таблицы доменов, которые обычно заполняются в самом начале и незначительно изменяются в период жизни приложения. Для приложения, связанного с покупками, это охватило вы характеристики заказа закупки, такие как "единица хранения". Для приложения кадровой системы это включало бы такие таблицы доменов как коды должностей и их описания. Эти таблицы могут вообще считаться постоянными через какое-то время после их начальной загрузки. Таким образом, когда вы вычисляете увеличение таблиц, то не должны учитывать влияние времени на размер этих таблиц. Фактически, вели период времени для аяализа достаточно велик, вы можете часто полностью игнорировать яти таблицы для целей вычисления размера, существенно упрощая задачу. Суммируя все, можно сказать, что отдельное OLTP-приложение будет часто иметь от двадцати до сорока таблиц. От двух до пяти основных таблиц будут расти с постоянной скоростью и будут занимать основную часть требуемого пространства. Некоторые поддерживаемые таблицы доменов будут расти с более медленной скоростью. Другие таблицы доменов первоначально загружаются, и их рост (а часто и сам размер) через какое-то время становится незначительным. Рост OLAP-таблиц OLAP-таблицы также, как ожидается, будут растя. Этому может быть много причин — будет влиять количество данных, поступаемых от OLTP-ВД, или может меняться число предварительно вычисляемых запросов. Рассмотрим эти типы роста. Рост пакетов OLAP-ВД, из-за их часто денормализованного характера, будут иметь меньшее количество таблиц доменов и большее количество итоговых таблиц, которые подвержены росту в зависимости от увеличения пакетов программ. Как правило, OLAP-ВД будут иметь одну иа двух форм. Большое оперативное хранившие данных будет иметь ряд больших итоговых таблиц е некоторыми поддерживающими таблицами доменов, Бели не исиользувтея специализированный механизм OLAP, некоторые OLAP-ВД будут иметь схему звезды, одну большую итоговую таблицу с лучами, направленными к ряду таблиц доменов. Объемный рост OLAP-ВД имеет множество факторов, такие как основные данные и их рост, степень денормализации, необходимая для ускорения запросов, и частота, с которой данные вводятся в БД из источника или источников. Почти весь рост может быть определен как дискретные события. Наиболее очевиден рост БД из-за обычных пакетов программ, поступающих из одного или большего количества источников. Каждый источник должен быть проанализирован на предмет периодичности загрузки пакетов и величины роста при их трансляции в итоговые таблицы OLAP-БД. Не следует это путать с вводом данных в программу из различных источников, обеспечивая денормализацию и формирование итоговых данных, которые часто используются в OLAP-БД. В дополнение к тому, что данные могут быть просто загружены, они будут также получены в итоге выполнения "временных пакетов". Например, таблицы с различными уровнями детализации могли бы выть загружены для периодов, охватывающих квартал, год, предшествующий год и период до предыдущего года. Следует осторожно относиться к оценке фактора потенциального геометрического роста, который может влиять на рост OLAP-БД.
OLAP-ВД широко используются для объединения неоднородных источников данных, чтобы выполнить анализ. Наряду с этими несоизмеримыми источниками,- вы должны будете оценить частоты загрузки их несоизмеримых пакетов. Пакеты могут бы» от ежедневных до ежеквартальных в зависимости т источника и допустимого времени ожидания в ваших требованиях по формированию отчетов. Рост в связи с ростом компании Менее очевиден для роста OLAP-ВД рост компания. Так как OLAP-БД часто используются для управленческих сообщений, БД должна иметь размер, чтобы не только учесть рост ее первоначальных источников, но также и рост из-за слияния компании и приобретений родительской компании. Например, медицинская компания страхования могла вы построить OLAF-БД, предусматривающую прибыль и потери передач, учитывая требования от конкретных географических областей, Когда приобретается другая компания страхования, охватывающая отдельную географическую область, сразу же исходам ВД должна приспособить данные от этого приобретения, чтобы дать точный отчет о прибыли и потерях для новой, расширенной компании. Здесь, в соответствии с нашими представлениями относительно роста, из-аа слияния компаний и приобретений, вы могли бы получить хороший результат, рассматривая проектирование структур ОЬАР-таблиц в довольно общей манере с таблицами, содержащими переменные столбцы. SQL 2000 предоставляет тип данных SQL_variant, и это именно то место, где вы можете воспользоваться преимуществами данного типа. Безусловно, при использовании столбцов переменного размера и столбцов типа SQX_variaac затрачивается большее количество маета, чем в случав использования простого целого числа или большого целого числа для того же самого представления, Кроме того, некоторые функции (AVG, SUM и т. д.) не могут использоваться для столбцов типа SQL variant. "Нам нужно больше отчетов!" Помните также, что OLAP-БД играют жизненно важную роль в получении своевременного и полезного анализа информации основными лицами, принимающими решение в организации. Поскольку данные меняются и принимаются деловые решения, здесь, май. нигде, возникает потребность в новых отчетах и информации, которая их обеспечивает. Обеспечение этой потребности часто означает новую обработку данных или новый столбец я/или индекс для существующей таблицы. Не забывайте об индексах В OLAP-БД влияние индексов яа объем не может не учитываться. Отдельная таблица с десятками гигабайт данных может иметь соизмеримое количество данных, необходимых для индексов. Таким образом, поскольку обсуждается вычисление объема таблицы, одинаково важно обсудить и вычисление размеров индекса. Вычисление полного размера таблицы Как вы можете ожидать, размер таблицы грубо равен произведению числа-строк на размер строки. Однако множество факторов увеличивает сложность вычисления. Ояи. включают число и тип индексов, индексные ключевые столбцы, число ж процент использования столбцов переменной длины, а также размер «кета или рисунка BLOB - дан ных.
Можно использовать следующие шаги, чтобы оценить количество места, требуемого для хранения данных таблицы, 1. Вычислить область, используемую даияыми в таблице, 2. Вычислить область, используемую кластерным индексом, если он существует. 3. Вычислить область, используемую каждым некластерным индексом, если таковые существуют. 4. Объединить аги значении вместе. SQL Server Books Online хорошо объясняет, как таблицы и индексы сортируются но размеру на основе этих параметров. Что сюда ие включено, однако, тик это программный метод для выполнения утомительной работы по свору информации относительно размеров столбцов, характеристик столбцов и индексных ключевых столбцов, а затем выполнения вычислений. Мы включили в эту книгу набор из трех хранимых процедур для выполнения данной работы, и вскоре будем с ними работать. Эти три процедуры (объясненные подробно ниже), которые нужны для создания мастера ВД, следующие: Q sp_table9celcDataSpace (хранимая процедура -расчета объема данных таблицы); О sp_indexSgetKeyData (хранимая процедура получения индексных ключевых - данных); □ sp_tableS indexSpace (хранимая процедура получения размера индекса таблицы). Чтобы использовать процедуры, вы сначала должны фактически создать пустые таблицы и индексы с помощью мастера ВД. Дополнительна вы должны подумать об ожидаемом размере строк и проценте заполнения столбце» переменной дяины. Эти процедуры были разработаны для SQL 7 и SQL 2000 и не будут иметь силу для 8QL б.В. Вычисление размера данных Сначала мы должны вычислить область, иоиользушую данными таблицы. Для наших целей мы предположим, что вы уже создали таблицу с ее соответствующими столбцами и индексами, Альтернативно вы можете работать, используя действия, кшс показано в конце главы. Если вы имеете столбцы переменной длины типа varchar, nvarchar и var.-._.-ary, то для-иаших целей вы должны будете сделать оценку процента, на который ати столбцы будут заполнены. Для процедуры sp_table8calcDateSpace, как мы увидим ниже, это — единственное значение для всех столбцов. Это единственное значение для всей таблицы мы будем хранить в переменной UvariFillPercent [процент заполнения столбцов переменной диины). С добаваеннем определенных пользователем расширенных свойств (обсуждвиных в главе 10) в SQL 2000, вы можете теперь создавать свойство peraentFiilExpectation для каждого из ваших столбцов переменной длины, которое программно достувио. Это — только одна из многих идей, когда разработчики ж администраторы ВД могут использовать эти определяемые пользователем расширенные свойства.
Размер таблицы определяется числом столбцов, длиной этих столбцов и числом строк. Вначале нам нужна некоторая исходная информация. В этой секции мм рассмотрим подготовительную работу для использования процедуры gp_table$aalcOataSpace. Предполагается, что объявлен ия переменных заданы. Размер страницы по умолчатаю для SQL 2000.— 8192 байта. Вы можете получать это программным путам, иетольауя следующий запрос: Ц SELECT tpageeiza - low FROM master,dbo,ept_values WHERE number •* 1 AUD type-- 'E' Для целей планирования вам нужно оценить число столбцов, которые ожидаются в таблице. Если вы ходите использовать по умолчанию число уже имеющихся в таблице столбцов, то можете получить это программным путем с помощью следующего запроса: I SELECT в sowcount - rows from sye indexes WHERE object_name(id) - ftablenanie AUD indld IN (0, 1) Далее нам нужно число столбцов переменной дайны, размер этих столбцов переменной длины (который является функцией их максимального размера, умноженного на фактор ожидаемого заполнения — 0variFlllPeraent), общее число столбцов иразмер фиксированных столбцов. Следующие два запроса возвращают эти результаты, основное различие в запросах заключается-в значении переменных столбцов из таблицы systypes: SELECT evaricolcount = COUKT(c.name), Smaxvaricolsize - ROUND ((BwriFi 11 Percent + ISNULL(SUM(c.length), 0) FROM syscolumna c JOIN systypes t OK c.xusertype - t.xuaertype JOIN sysobjects a on c.id • o.id WHERE t.variable - 1 AMD o.nane “ Btablename В GROUP ЕЕ о.паяе SELECT icolurancaunt - COUNT(c.name) + ivaricolcount, S «fixcolsiza - iSNOLb(SOM(case when c.length is toll then t.length ELSE c.length END), 0) В FROM syscolumns c JOIN systypes t CM c.xusertype « t.xusertype В JOIN syeobjecte ~o ON c.id - o.id where t.variable - 0 AND ©.name =- ttablanaee Часть каждой строки резервируется для определения возможности использования HULL в каждом столбце. Она содержит по биту для каждого столбца плюс некоторая дополнительная величина. Эта. часть строки называется битовым массивом значений TOLL. Нам нужна целая часть рассчитанной величины, и запрос для расчёта следующий: SELECT tnullBiteap - 1 * FLOOR((eeolumncount + 7) / S) Если имеются какие либо столбцы переменной длины, мы подрегулируем максимальный размер столбца переменной дайны для строки, добавляя два байта для таблицы и два байта для каждого столбца переменной длины: И SELECT emaxvarieolaize = tmaxvaricolsize + 2 + gvaricolcount * 2 Полный размер строки — сумма размера фиксированных столбцов, размера битового массива значений NULL, максимального размера переменных столбцов и заголовка строки данных в четыре байта: В select Stnaxrowsize - efixcclslte + gnullbitmap * gmaxvaricolsize + 4
Затем мы должны разовраться, представляет ли таблица "кучу” строк или организована в соответствии с кластерным- индексом. Исе последнее верно, то мы должны вычислить количество свободного места между строками из-за того, что фактор заполнения в этом случае меньше чем 100. Каждая таблица всегда имеет строку в параметре sysindexes с индексом Id, равным 0, для таблицы, представляющей собой "кучу" вез кластерного индекса, или индексом Id, равным 1, для таблицы, физически сортируемой в соответствии с кластерным индексным ключом. Очевидно, если таблица представляет собой "кучу” без фактора заполнения, нам ве нужио определять фактор заполнения и вычислять свободное пространство. Мы получаем фактор заполнения с помощью следующего запроса: IF ({SELECT India FROM sysindexes WHERE object_namefid) - ftablename AND indld I» W, IH - 1) SELECT gfillfactor - orlgFillFactor FROM sysindexes Ц WHERE abject_neee(id) - gtablename AND indid - 1 Значение по умолчанию для фактора заполнения — 0; допустимые значения находятся в диапазоне от 0 до 100, Значение фактора заполнения, равное 0, не означает, что страницы заполняются на 0 процентов. Это трактуется подобно фактору заполнения, равному 100, при котором SQL Server создает кластерные индексы с полными страницами данных и некластерные индексы с полными страницами-листьями. Это отличается от 100, так как SQL Server оставляет некоторое свободное место на верхних уровнях дерева индексов. Для целей вычисления свободного пространства данных в таблице фактор заполнения 0 будет идентичен фактору заполнения 100. Вели мы имеем таблицу “кучи", то продолжим формулу дая вычисления количества свободных строк на страницу, регулируя фактор заполнения до 100. Вели мы имеем фактор заполнения таблицы, организованной на основе кластерного индекса, то вычисляем количество свободного места, помещенного в каждую страницу: Ц SELECT ffreeHowePerPage - (gpagesize » 1100 - gfillfactor) / 100- / (gmaxrowsize - 2)> Заметьте, что, если фактор заполнения — 100, вышеупомянутый запрос будет всегда иметь нулевое значение для числа еаоводиых строк иа страницу. Это истинно независимо от того, является ли таблица таблицей ’кучи" или организованной на основе кластерного индекса с фактором заполнения О или 100. Прежде, чем мы рассмотрим следующий запрос для вычисления полного размера данных в таблице, необходимо небольшое разъяснение. Встроенная в этот запрос формула определяет число строк на странице. Оно программно представлено выражением FLOOR ((gpagesize — 96) / (Bmexrowsize + 2)). Это — место на странице, не используемое указателями на строки заголовками строк — ipagesize - 96, поделенное на размер строки плюс два байта между строками — 9ma*rowaize + 2. Далее мы используем функцию FLOOR, чтобы округлить частное до ближайшего целого числа, так как строки не могут перекрывать страницы, и полная строка должна находиться на одной странице. Иа этого результата мы вычитаем число свободных строк на страницу. На полученное число полезных строк на страницу затем делится оцененное или фактическое число строк, полученное ранее, чтобы получить общее количество страниц, которые нужны для наших данных. Округление, использующее функцию CBILIHG, необходимо для получения полных страниц. Вы могли бы остановиться в этот момент, если вас интересует только число нужных страниц. Умножение на фактический размер страницы дает число байт, которые нужны для данных. I SELECT &date_space_u»ed - gpagesise • CEILINGCBrowcountZ((FLOORi(gpagesize — 96) f (taaxsowsize т 2))) - BfreeRowsPerPage))
Повторное рассмотрение индексируемых структур в виде В-дерева Прежде, чем мы начнем вычислять область для индекса, полезно рассмотреть, как индексы оргваизуютея. Индексы, по существу, представляют сокращенные указатели на строки в ВД. Все индексы содержат, по крайней мере, один уровень, уровень корневого узла. Это — единственная страница, где начинаются запросы индекса, Если таблица не настолько маленькая, что страница корневого узла является также страницей узла-листа нижнего уровня основания нек.гастернс-го индекса, корневой узел укажет на следующий уровень в индексе. Это может быть страница уровня узлов индекса или страница одного и только одного уровня узлов-листьев. Каждый уровень узлов индекса указывает на более низкий уровень умов индекса, пока не будет достигнут уровень узлов-листьев. Как вы можете видеть из рисунка ниже, этот пример индекс и имеет три уровня: уровень корневых узлов, единственный уровень узлов индекса и уровень умов-листьев. Уровень узлов-листьев может принимать одну из трех форм. Ваш индекс — кластерный индекс, уровень узлов-листьев — фактичеише данные, отсортированные в соответствии с кластерным индексным ключом. Оставшиеся две формы узлов-листьев — для нвкластерных индексов и зависят от того, имеет ли тавжица кластерный индекс или нет. Если индекс — некластерный индекс таблицы с кластерным индексом, каждая строка,уровня узлов-листьев будет содержать указатели ив соответствующий кластерный индесиый ключ для этой строки. Таким образом, размер каждой строки уровня строк-листьев в этом случае — по существу размер кластерного индексного ключа. Здесь следует сделать краткое упоминание, что это —- основная причина для хранения кластерных индексированных ключей, очень коротких. Когда запрос проходит через некластерный индекс такой формы, он должен затем пройти кластерный индекс, чтобы найти фактическую строку. Помните, что чем короче ваш индексированный ключ, тем меньшее количество уровней будет иметь ваш индекс. Третья форма узла-листа —• для нвкластерного индекса у таблицы, не имеющей кластерный индекс (известной также- как таблица "кучи"). Каждая строка на уровне строк-листьев является указателем с восемью байтами (четыре байта для указателя страницы и четыре байта для смещения строки на странице) на фактическую строку таблицы.
Подытоживая этот раздал, можно сказать: □ Для кластерных индексов страницы узлов-листьев являются страницами данных. О Для некластерных индексов в таблице с кластерным индексом, страницы узлов-листьев содержат указатели на кластерный индексированный ключ. Q Для некластерных индексов в таблице» не имеющей кластерного индекса, страницы узла-листа содержат указатели на фактические строки таблицы. Когда индекс имеет меньшее количество уровней узлов, оа называется "выгодным". Вы можете видеть, что выгодные индексы означают, что меньшее количество страниц читается при запросе, так как каждая уровень узлов представляет читаемую страницу. Меньшее количество читаемых страниц означает меньшее количество дорогостоящих дисковых операций и меньшее количество циклов ЦП. В целом, выгодные индексы лучше, когда вы должны довраться к денным таблицы, чтобы получить нужную информацию. Концепция рассмотренных имдвкеов дает компромисс, упомянутый в другом месте этой книги. Вычисление размера индекса Область, занимаемая данными в таблице, —- только половина уравнения для получения общей области, используемой таблицей. Другая половина —- область, занимаемая индексами. В OLAP-ВД неудивительно, когда область, занимаемая индексами, равна или превышает область, используемую фактическими данными. Мы рассмотрим еще две хранимых процедуры, sp_table5indexSpace (хранимая процедура определения области, занимаемой индексом таблицы) и sp_index$getKeyBata (хранимая процедура получения ключевых данных индекса). Первая, ep_tableSindexSpace, является главной процедурой, в то время как sp__indexSgetKeyData — вспомогательная процедура., вызываемая первой. Как было отмечено выше при вычислении размера таблицы, мы делаем упрощение, оценивая процент, который будут занимать столбцы переменной длины. Как и выше, предполагается, что переменные объявлены. Когда вы запускаете процедуру sp_tableSindexSpace, в дополнение к полному размеру таблицы вы получите информацию относительно размера одних данных в таблице, размере каждого индекса, и сколько уровней узлов B-деревьев необходимо для данного числа записей в таблице. Мы начнем наш анализ с помощью sp_table$indexSpace, выполнив некоторую вспомогательную работу. Нам понадобятся размер страницы для сервера, число строк таблицы, если оно не задано в качестве входиого параметра, и полная область, занимаемая только данными. Запросы получения размера страницы и числа строк были рассмотрены ранее в разделе Вычисление размера данных. Чтобы получить область, используемую фактическими данными, мы вызовем хранимую процедуру sp_table$calcDataSpace, которая рассмотрена выше. Затем мы создаем временную таблицу #keyColumns (ключевые столбцы) со столбцами, содержащими информацию для идентификатора индекса, названия столбца, дины столбца, имеет ли столбец перемеияую дайну, и фактора заполнения индекса с заданным идентификатором. Мы передаем имя этой таблицы хранимой гфоиедуре spindexSgetKeyData, чьей единственной задачей является заполнение таблицы #keyColивuis.
В sp_lndex5getKeyData курсор определяется идентификатором индекса для каждого индекса независимо т- того, является ли ои в таблице кластерным или иекластерным. Если нет никаких индексов, 0 в fetchstatus (состоя-ние выборки) для курсора будет меньше нуля, и мы вернемся к главной процедуре, иначе мы пропустим курсор через все идентификаторы индекса. Каждый индекс будет всегда кхеть по крайней мере сдан столбец. Чтобы получить данные этого столбца и поместить их в таблицу tkey Cd дате (имя которой выло передано в процедуру как переменная вtercpTableNane (таен временной табнвды))' жл динамически формируем строку символов и отрабатываем ее. Обработка динамической строки символов — большое достоинство мастера, так что стоит рассмотреть выполнение запроса ниже поподробнее. В переменную eexct_stmt (выполняемый оператор) помещается оператор USE для определения имени ВД. Затем мы формируем оператор INSERT для временной таблицы ♦keycolumns (передаваемой в переменной gtempTableName) и ее столбцов andid (идентификатор индекса), colname (имя столбца), variable (является ди столбцом переменной длины), collength (длина столбца) и f ill_factor (фактор заполнения). Данные, загруженные в эти столбцы, являются идентификатором индекса в месте расположения курсора (3 indid.), именем столбца, формируемым функцией INDEX_COI«, значением переменной для столбца datatype (тип данных) из таблицы systypea (системные типы), длиной столбца из таблицы syscolumns (системные столбцы) и значением фактора заполнения, который также является частью ключа курсора. После того, как мы загружаем строку в переменную 8ехсc_stmt, мы просто выполняем ее внутри набора круглых скобок. SELECT gexet_stint ~ 'USE ’ + quotename (fidbNatne, ' i' > + ' INSERT ' ♦ quotename(8tempTableHa»e, ' J' J + (ondid, colname, variable, collength, ft 1_fec-tor) SELECT ' + convert (varchar (10J, Undid) + *, :NDEX_COLf* *- quotenane(etablename, + ', ' + convert(varchar(10}, Bindid} + ‘ , lb t.variable, c.length, ' +• convert (varchar (3), 9f xll__factor> -r ' FROM ' 4- edbnwtse + ’ . dbo. syscolumns c JOIN ’ + вЬЬпаие + ' .dbo.systypes t ON c.xusertype - t.xusertype WHERE c.id - ’ + convert(varchar(16), Bobjid) + ' AND c.name “ INDEX_COL(’ + guotaname (ttablenawe, + + convert(varchar(10), Undid) + 1)' EXEC (eexct_etffitl Если вы не понимаете, как выполнить строки символов, будет полезно рассмотреть эту процедуру более подробно. Откройте код для процедуры и SQL 2000 Query Analyzer. Цветное оформление кода в Query Analyzer будет очень полезно для понимания, что мы делаем. Заметьте, что, так как мы используем функцию INDBX_COL, мы не должны делать запрос к таблице SYSINDEXES. Поверьте нам, что иет нужда вызывать SYSINDEXES, чтобы получить имена столбцов. Функция INDEX_COL более чем достаточная. После получения имени первого столбца мы используем для INDEX_COL поле идентификатора инкрементного ключа, чтобы получить имя следующего столбца. Если имя столбца, возвращаемое функцией INDEX_COL, имеет значение NULL, это оаначает, что мы закончили рассмотрение столбцов для конкретного индекса, и переходим к следующему индексу в курсоре, если он имеется. Если имя столбца, возвращаемое функцией INDEX_COL, не NULL, мы организуем цикл WHILE, обрабатывая строку символов подобно описанному выше, чтобы поместить следующую строку в #keyColunms.
Когда мы используем последний столбец для последнего индекса, то возвращаем управление процедуре sp_table$indexspace е теперь уже заполненной таблицей tkeyColumns, Сначала мы проверяем tkeyColurans, чтобы узнать, имеет ли таблица кластерный индекс. Если ои есть, мы устанавливаем в нашей процедуре флаг, указывающий, что имеется кластерный индекс. Это важно по двум причинам. Вспомните, что из рассмотрения выше структуры индекса, уровень узлов-листьев для кластерного индекса представляет данные. Мы уже имеем эту информацию из процедуры sp_tableScalcDataSpaсе. Во-вторых, если мы имеем кластерный индекс, каждая строка на уровне листьев любых некластерных индексов будет иметь размер кластерного индексного ключа индекса, а не указателя строки в 8 Яайтов. Вот как мы проверяем наличие кластерного индекса, чей идентификатор индекса всегда равен 1: g] IF EXISTS (SELECT 1 FROM ikeyCoXumns WHERE indid = 1) SELECT 8c*us_flag = I Далее мы начинаем перемещаться по дереву в зависимости от условий, вычисляя для уровней, не представляющих листья, размеры кластерных индексов или сразу же продолжаем вычислять размер каждого некластерного индекса. Так как мы можем иметь до 249 некластерных индексов, то устанавливаем курсор для работы е каждым некластерным ивдекеом на основе идентификаторов индексов, размещенных в Т8блице#кеуСо1итпз. За исключением уровня листьев вычисление размеров других уровней для всех индексов одинаково. Мы начинаем вычисление с размера ключа для индекса. В странице индекса длина ключа дает размер строки. Это очень похоже на вычисление выше размера строки с помощью процедуры sp_table9calcDataSpace, и мы ие будем рассматривать конкретные детали, однако отметим, что данные в этом случае поступают из сформированной ними таблицы ♦keyColumns, а ие на фактических таблиц системы, как в случае процедуры sp_tableScalcDataSpace. Имеются три вида подсчета в процедуре sp_table$indexSpace для соответствующих вариантов размера строк кластерного индекса и некластерного индекса в случае таблицы с кластерным индексом и таблицы "кучи" без кластерного индекса. 8Flxed._CKey_Size + 8Variable_CKey_Size ~ 9CIndex_Null Bitmap +1+6 eFixed_Key_Size + evariable=Key=Size + eindex_Null_Bitniap + 1 + @CIndex_Row_Size eFixed_Key_Size + 8Variable_Key_Size + eindex_Mull_Bitn>ap + 1 + 8 Заметьте, что в последних даух случаях для размера строки иекластерного индекса различие в суммах определяется различием между размером строки кластерного индекса 8Clndex_Row_Size и значением указателя в таблице “кучи” в 8 байтов. Кроме того, заметьте, что в первом случае размер строки кластерного индекса, ®CIndex_Row_Size будет всегда, по крайней мере, 10 байтов и, что более вероятно, не крайней мере, 13 байтов в размере. 13 байтов имеют место, когда ваш кластерный индекс единственный столбец целых чисел в 4 байта. Некоторые думают, что могло бы быть лучше, если ие иметь кластерный индекс. Вовсе нет! Когда сделано разумно, кластерный индекс может дать существенное улучшение работы. Что мы должны оделять,, так -это обеспечить размер ключа кластерного индексе и размер строк настолько малыми, насколько получится, благодаря эффекту влияния размера строки некластериого индекса.
Всегда думайте об этом. Уменьшение длины ключа вашего кластерного индекса на один байт приведет к сокращению одного байта для гаадой отдельной строки каждой отдельной страницы каждого отдельного уровня каждого отдельного индекса вашей таблицы вне уровня уз.тов-дметьев кластерного индекса. Все складывается. Это означает более простые индексы и более быстрые запросы. После того, как мы имеем длину ключа для индекса, нужно сначала вычислить размер уровня листьев (или в случае клаетермого индекса уровня индексов непосредственно выше уровня листьев). Если ятот уровень имеет только одну страницу, мы на этом завершаем. Если здесь размер больше, чем одна страница, мы должны настелен но вычислять следующие уровни, пока не получим ситуацию, когда уровень представляет только одну страницу в размере. Этот уровень по определению является уровнем корневых узлов. Для организации цикла расчета страниц уровня индексов здесь используется код цикла WHILE, проверяющий, занимает ли конкретный уровень только одну страницу в размере: SELECT ethie_Eui#_₽ages_Level ~ CEILING ((SNuB-Rows / exndex_RwS-₽er_P«ge - lFree_Index_Rcw5_E:er_?acel) SELECT еншв_1пйек_₽вдез « ®this_Kura_?ages_Level WHILE IthiS—Hura_₽agee_Level > 1 BEGIN SELECT glewel_csBt - eievel_ent + 1 SELECT ethiS—Wua_₽ages__Level - CEILING{ethie_Kua_Pages_Level / вМ^ГййвЯ-Ввиа^РвГ—Pagal SELECT eHum_lndex_Pagea = gNue_Index_Pages + 0thiS-ltaa_Pa<jes_bevel END Сначала мы вычисляем число страниц для текущего уровня —fthis_Nim_Pages_Level. Мы уже имеем инициалиаироваиву» переменную Sievel_cnt со значением 1. Добавляем ethis_Num_Pages_Level к полному значению, которое получаем яяя числа страниц в индексе — 8Num_Index_Bages. Вели 0thls_Nua_Pages_Level вольте единицы, организуем цикл и определяем число страниц для каждого последовательного более высокого уровня индекса, пока не получим корневой уровень, состоящий из единственной страницы. Вычисления для кластерного индекса аналогичны. В данный момент мы определили число страниц, необходимых для каждого индекса, н число уровней В-дерева для каждого индекса. Далее выводим эту информацию и добавляем ее к полному числу страниц, которое подучаем для всей таблицы. После завершения работы с индексами добавляем число страниц индексов к числу страниц данных, чтобы установить полный размер таблицы. Очевидно, что через какое-то время в таблицах и индексах данные увеличиваются. Имеется ли какой-либо способ разумно уменьшить число записей в таблице и число уровней в ваших индексах? Мы обсудим это в разделе ниже, посвященном архивированию данных. Размер загрузки транзакции После вычисления оценок для размеров веет ваших таблиц, нужно также о^ждалить - размеры загружаемых частей транзакций. Обычно загружаемые части транзакций первоначально аадаются н виде процента от размера ВД. Этот процент прежде веете определяется ожидаемым уровнем транзакции и скоростью, а которой загруженные чести транзакции сбрасываются ив диск или и»- ленту.
Для OLTP-ВД, где транзакции — хлеб и масло ВД, неудивительно видеть размеры загрузки транзакций, превышающие ВО процентов от размера ВД. OLAP-ВД, с другой стороны, обычно работают с 10 или менее процентами заданной загрузки транзакций. Это вызывает проблему восстановления загруженных частей транзакции, так как возможность восстановления будет влиять на их размер. Для больших загрузок пакетов в SQL SERVER 7.0 часто разрабатывается процедура, куда помещается загруженная часть транзакции в режиме truncate log on checkpoint (усеченная загрузка в соответствии с контрольными точками), когда -загружается пакет. Разработчики SQL Server 2000 учли этот процесс в добавили опцию Bulk-Legged (массовая загрузка) для процессе загрузки транзакции. Модель воеетановлеиия Full (полная) означает, что конкретная транзакция, включая массовые загрузки и операторы SELECT INTO, может выть восстановлена отдельной транзакцией. Она устанавливается для больших загрузок транзакций, оеовенво, для OLAP-ВД, которые имеют большие пакеты транзакций. Модель Bulk.-Logged (массовая загрузка) — компромиссная, обычно резервируемая для OLAP-БД. Здесь для массовой загрузки, операторов SELECT INTO и программ массового копирования записываются пакеты загруаки вместо отдельных транзакций. Это может означать, что в случае отказа целые пакеты транзакций должны выть повторены вручную, но достоинством является меньшее число загрузок транзакций. Модель восстановления Simple (простая) аналогична установке БД в truncate log on checkpoint (усеченная загрузка в соответствии с контрольными точками) в SQL 6.5 и 7.0. По существу, БД, восстанавливаемая к последней контрольной точке, используется для полной совместимости с предыдущими версиями и не применяется больше в этой модели. Она обычно рекомендуется для малоэффективного оборудования.
Так какие же загрузки транзакций достаточны? Обычно начинают а небольших, а диспетчер автоматически увеличивает файлы восстановления транзакций. Мы рекомендуем начать с 20 процентов от ожидаемого размера данных для OLTP-ВД и с б црацежрв для OLAP-ВД в . режиме восстановления Bulk-Logged, Архивирование данных в случае необходимости Взяв все эти источники данных, можно быстро заметить, что даже в компании умеренного размера объем дан»» может стать огромным. Это особенно характерно для денормализованных- OLAP-систем. На ранних стадиях объемного анализа размеров должно быть сделано решение, нужно ли и когда нужно а^хю^вяюь данные отдельно от основной БД в контейнере хранения, характеризуемом общим термином "автономный": лента, накопитель с однократной записью и многократным считыванием (Write-Once, Read Many — WORM), CD-ROM, DVD ... Хотя данные могут больше и не быть необходимы для сообщений или обработки с помощью транзакций, может быть юридическая или распорядительная причина сохранять данные. Вы должны помнить, что в то время как эти инструкции очень часто определяют требование относительно времени хранения данных, они редао определяют средства хранения (бумажная твердая копия, хранилище только для чтения, вращающийся диск, глиняная или каменная табличка...), на которых размещаются данные. Может оказаться очень дорого держать массивные количества данных, вращающихся в течение многих лет, когда могут быть просто "заскладированы" на носителях, используемых только для чтения. Даже если данные не огромны, и они могут асе еще оставаться в БД ва SQL Server, вее-таки может иметь смысл архивировать их отдельно от ваших основных таблиц. Как мы отметили ранее в книге, самые большие затраты, которые мы всегда стремимся минимизировать при использовании ВД, связаны с чтением с диска. Чем большее количество данных, которые мы должны читать с диска, тех_медлеявее приложение будет казаться конечному пользователю. Если чтение е диска считается медленным, чтение архивированных, автономно хранящихся даиных может выть на порядок (или белее) медлеяиее. Общее эмпирическое правило заключается в том, что никакие данные не должны выть архивированы с диска, если имеется обычная эксплуатационная потребность в доступе к этим даяиым. С другой стороны, если данные больше не являются оперативно нво^рдимы, настоятельно рекомендуется их заархивировать или в другую таблицу, или в другую ВД, или иа другой сервер, или даже в другую среду. Выигрыш от запросов с меньшими индексами и (что ’левее желательно) меньшими таблицами может выть огромным. Но что означает "обычная эксплуатационная потребность"? Акцент здесь должен выть на слове "обычная". Где к данным обращаются ежемесячно, ежеквартально или даже ежегодно, может оказаться лучше держать их на жестком диске. Необычные события подпадали бы под рубрику запросов информационно-поисковой системы, юридических исков и приобретений компании. Такие необычные события возникают редко. Когда они происходят, расход времени на загрузку данных на жесткий диск или в ОЗУ с автономного устройства хранения н настройка запросов, которая может понадобиться, — обычно приемлемые расхода СУВД и АБД (администратора БД). 19-1868 559
Стоимость архивирования Подовно всем усовершенствованиям в работе каждая оптимизация должна быть проанализирована с точки зрения выгоды стоимости. В то время как выгоды включают увеличенную производительность, затраты на архивирование в основном тройные, Во-первых, это стоимость подсистемы архивирования ж ее физическая реализация. Прежде всего она включает непосредственную стоимость, например, специализированного / т, иакошгтеля с однократной записью и многократным считыванием. Дополнительно, инженеры, обслуживающие сервер, должны получить, а затем поддерживать навыки для его реализации и обслуживания. Мы ве должны превратно истолковывать слово 'овсл^яшват-ъ’ в предыдущем предложении. Вее мы знаем, что быстрый темп изменений в программном обеспечении определяется темпом усовершенствования аппаратных средств ЭВМ. Если вы программист е большим стажем вроде метя, то можете помнить, что чуть больше десятилетия назад мы крутились вокруг процессоров TRS-80 и 5s/,-дюймовых (или еще хуже, 8-дюймовых) дискет. Подумайте, что требуется, чтобы загрузить и 1фочитать в-давймовые дискеты сегодня. Это даже ве входит в программное обеспечение форматирования дисков современных ОС, с - ' помощью которого оно было сохранено. Вообразите, что требуется хранить данные в течение 25 лет. Вы должны посчитать "затраты", связанные с возможностью нротатать данные древнего формата, или е периодическим переформатированием данных со старых носителей на более современные. Когда физическое устройство хранения архива существует, дополнительные и существенные затраты могут быть в связи в аккуратным удалением данных из активных ВД реального времени. Как никогда, необходам некоторый детальный анализ. Ноже упаси архивировать деловые данные, которые являются- оперативно необходимыми! Стоимость и время этого анализа должны быть учтены в вашем проекте. Архивирование данных не означает, что мы их просто удаляем; вы все еще должны быть способны использовать их. Наконец, имеется стоимость проектирования кода, необходимого для доступа к архивным данным. Может потребоваться необычная, особенная потребность в данных. Проектировщик архива доджей учесть и подготовить tee для таких необычных потребностей. Детали архивирования Теперь, когда вы полностью убеждены в необходимости архивирования, то должны разобраться с деталями. Очевидно, что каждая ВД в зависимости от параметров должна иметь разные критерии, которые я ранее сформулировал. Однако обычно нужно начинать думать относительно архивирования, рассматривая следующие моменты. □ Период архивирования. □ Архивирование таблиц фактов с разделением по датам. □ Архивирование при завершении сделок. □ Доступ к архивированным данным.
Период архивирования Вкратце, мы должны начать с овсуждения концепции таблиц фактов, Таблицы фактов обычно представляют собой самые большие таблицы в БД, потому что они содержат подробные данные для основных транзакций. Наиболее очевидный метод архивирования заключается в удалении старых по времени данных из таблиц фактов и размещении в параллельную структуру таблиц о архивированными данными. Хотя обычно непосредственно ясно, как, например, в случае "Мы архивируем все данные трехлетней давности", некоторые моменты могут быть ве столь очевидными. Иногда таблицы фактов могут иметь несколько связанных с ними дат. Например, запись деталей заказа аакупкн мажет иметь дату заказа закупки, когда он выл начат, многократные даты одобрения иа различных уровнях согласующих инстанция, дату, когда заказ закупки был представлен продавцу, дату подтверждения продавцом получения заказа закупки, дату или даты, когда заказ был выполнен, и дату, что сделка закупки была, наконец, закончена. Неудивительно, если проходит шесть месяцев или год, прежде чем некоторые заказы закупки будут наконец выполнены, особенно при организации контрактов. Так как же вы хотите архивировать — на основе даты, когда заказу закупки был назначен номер, или на основе даты, когда заказ закупки выл наконец выполнен? Это — работа квалифицированного аналитика данных, чья работа хорошо оплачивается, И этим специалистом могли бы быть вы. А что сказать относнтаано таблиц доменов, которые поддерживают таблицы фактов? Возьмем таблицу vendor (продавец), которая дает столбец ve&dor_id (идентификатор продавца) для записи деталей заказа закупки. Обычно вы будете иметь внешний ключ и записи заказа закушш, ссылающейся на первичный ключ в столбце таблицы vendor. Возьмем случай, когда продавец ушел из бизнеса. Теперь, тремя годами позже, вы должны заархивировать все строки, которые этот ключ записи продавца поддерживался вашими строками заказов закупок. Совершенно неожиданно, вы больше не можете программно обеспечить наличие первичных ключей с помощью строки, содержащей внешний ключ. Вы должны думать о таких вещах, поскольку собираетесь осуществлять архивирование. Как все ш знаем, хорошие программисты заставят это работать: лучшие программисты вьииишят это удаление корректно. Архивирование таблиц фактов с разделением по датам Многие из данных, которые мы помещаем я наши ВД, легко делятся по значениям дат. Например, сделки в банкоматах имеют очень точную дату, когда они выли проведены. Далее, как только они выли зарегистрированы, данные по существу используются только для чтения. Вместо возвращения и удаления спорной сделки, позже вводится другая сделка для корректировки. Таким образом, легко видеть, как все сделки за конкретный месяц вводились в течение месяца в конкретную таблицу. Наиример, таблица АТМ_200104 (или фактически подмножество датированных таблиц ео ссылочной целостностью,‘которые на самом деле охватили бы все сделки банкоматов) могла Лг содержать все сделки-вавкоматов в течение апреля в 2001 году. Как только апрель закончится, больше сделки не будут постуиать в эту таблицу. После формирования счетов за апрель 2001 года и итоговых таблиц для OLAP-анализа а» таблица — явный кандидат на архивирование на носителе только для чтения. Такие огромные таблиц» фантов обычно сгружаются на ленту, CD-ROM или DVD для нечастых обращения. Теперь, зная это, вы можете понять, почему банк запрашивает у нас изрядную штату за копию предыдущего ежемесячного отчета. В некоторых случаях компаиии просто откажутся формировать дубликаты записей в деталями из этих таблиц фактов (за исключением требований суда).
Архивирование таблиц фактов в соответствии с характеристиками Подобно архивированию таблиц фактов е разделением по датам конкретные строки могут быть также архивированы из текущих таблиц в соответствии е конкретным наваром условий для строки. В этом случае мы не отключаем вею таблицу Скорее, мы отбираем конкретные строки и группы строк из таблиц на основе заданных критериев. Хорошим примером могла бы быть сделка, завершенная больше чем два года назад. Очевидно, когда еделйа открыта и в нее помещаются записи деталей, вы вряд ли захотите ее архивировать. Даже после того, как она недавно закрыта, вы вряд ли захотели бы архивировать сделку, потому что она все еще используется для формирования отчетов и анализа на данный год и за весь В зависимости от ваших бизнес-правил, тем не менее, сделвв Волте чем двухгодитаой давности могли бы быть главной целью архивирования. Другим при-чером строки, которая могла вы быть архивирована на основе заданного критерия, будет строка, представляющая потенциального заказчика, который никогда не был рмлияожан. Вы могли бы шюп, таблицу заказчиков, внося в список всех ваших текущих, прошлых и предполагаемых заказчиков. Вы иепользусте эту табяицу ие только, чтобы записывать деятельность заказчика, но также и кек инструмент формирования продаж для отправок по почте и прогнозов. Обычно потенциальные заказчики помещаются в таблицы в виде списков, которые ваша компания покушает у брокера списков. Многие из этих иотенщильных заказчиков станут заказчиками. Многие не станут. Периожчески вы хотели вы сокращать этот список, чтобы делать ваши запросы более легкими. Вы определяете критерии для архивирования потенциального заказчика как "негодный материал”. Такими критериями могли бы быть "никогда не быв активным заказчиком” и "мы вносит его в список в течение трех лет без каких-либо действий е его стороны", Одаако помните, что вы заплатили хорошие деньги за этот список. Вы не просто удаляете этих потенциальных заказчиков. Они помещаются в расширяющуюся архивную таблицу негодного материала или таблицы на носителях, исподазуемлх только для чтения. Кто аиявт? Может жщйдебиться пернуть к жизни этот негодный материал и вернуть строку назад в катеторшо активных заказчиков. Или вы можете создать такой список, который можете продать другой компании и возместить часть стоимости первоначального списка. Веди бы вы удалили данные, то вынуждены были бы выполнись дорогой процесс ручного восстановления данных. Может быть намного дешевле сделать относительно медленный, но дешевый запрос к пипгей архивной таблице. При проектировании БД тот момент, когда вы начинаете думать относительно размещения большого количества данных в таблицу, является одновременно и моментом, когда вы должны начать думать относительно архивирования данных из таблицы. Важно заметить следующее: архивирование же должно быть запоздалой мыслью ври проектировании ВД и приложения. Архивирование может представлять существенную область преимуществ ио время функционирования системы, Ввщт, однако, ваша структура таблиц ие позволяет поддерживать архивирование, или приложения не были разработаны так, чтобы иметь доступ и архивным данным, вы мажете этого и не получить. Будет очень трудно продать заказчику идею относительно удаления данных из его ВД, если требуется оплатить стоимость перепроектирования приложения или БД ж дать вам большее количество денег, чтобы сделать это, как только исходаое приложение начнет работать. Еще хуже, когда ваше приложение начинает задыхаться от данных, которые в противном случае должны быть архивированы, и заказчик в этом обвинит вас.
Доступ к архивированным данным Итак, вы теперь тщательно рассмотрели и забрали данные из ваших больших таблиц фактов или удалили таблицы фактов полностью от активного использования в случае таблиц фактов с разделением ио датам. Два вопроса приходят на ум: как вы вернетесь к этим данным, когда они вам понадобятся? И если вы сгрузили данные на альтернативные носители, что является сроком их годности? Относительно первого вопроса можно сказать, что приложение часто будет создана так, что оно будет знать о наличии архива, ветвиляя м ал енькую строку в активную таблицу, я в л я ющу юся ключом к архивированной строке. Когда пользователь запрашивает эшу архивную строку, процесс заменяет ее в запросе. Эго может включать простой запрос к таблице, дасковад, загружающий CD-ROM, сообщение, поелаияае дежурному компьютерной жаоиш, чтобы установить ленту, ияи сообщение, посланное нязят иеходаому пользователю, запрашивающее его, если он реаль&зй пользователь, действительно ли ему нужны эти данные. Многое в этом процессе зависит от того, как данные архивированы. Если архивированные данные все еще в БД, но только перемещены в другое .место (так, чтобы запросы основной таблицы фактов были быстрее и индексы проще), то получение данных будет почти незаметно для пользователей. Другой крайностью является случай, когда архивированные данные размещены ни. магммтных лентах, которые должны быть установлены вручную, и конечный пользователь может решить, что они иа самом деле ему m нужны. Второй вопрос, который нужно иметь в виду при архивировании данных, ставит задачу хранения данных на альтернативных носителях и их срока годности. Все чаще ори архивировании данных ояи помещаются или на CD-ROM или на DVD-ROM. А что, если они помещены на магнитную ленту? Мы недавно натолкнулись на. историю, связанную с Национальной воздухоплавательной я космической администрацией (NASA) в Совдииеивых Штатах. Спутники NASA обычно разгружают информацию иа ленту для новлвдующето изучения и анализа учеными в финансируемых. NASA лабораториях. Количество данных потрясающее. Оказывается, что эта лента имеет срок годности 10 дет. Также оказывается, что, если взять леитоцротикные устройства NASA, которые сегодня являются пригодными к эксплуатации, то чтобы начать перекоиироваиие этих данных на более я»ди«я>« и более долговечные средства информации, потребуется значительно больше 10 лет. Короче говоря, будьте осторожны в выборе, где вы будете хранить ваши архивированные данные, если вам потребуется использовать их позже. Характеристики сервера Мы могли бы представить сложный анализ поломок памяти, чтобы точно определить, какие части ОЗУ занимаются какими частями SQJL Server. Эта тема, однако, исчерпана другими томами, особенно Угюй Mtero&ift SQL Server 2090“ (Microsoft Press, ISBN 0735609985}, Мы ограничим наш аиалиа кратким обзором, подчеркивая, где вы можете получить самый большой выигрыш в хврактеристйквх. Подсистемы памяти Короче говоря, если имеются какие-дабо деньги, оставленные в бюджете для ваших аппаратных средств, первое правило в настройке характеристик SQL Server — использовать «я деньга м ОЗУ. SQL Server процветает -ие хорошея ОЗУ. Ьи ш углубимся в возможности улучшения функционирования дисковых подсистем, то можно сделать илрдг’иокоиж.оиредвяяотея- - - величиной ОЗУ, которое дает максимальяый эффект больше, чем у дисковых подсистем. see
По этой причине вы должны серьезно рмсматриввкь получение максимального объема ОЗУ, которое может обеспечивать пята материнская плата. Если вы ив делаете так, удостоверьтесь, что покупаете чипы ОЗУ, которые являются максимальными для каждого разъема для установки ОЗУ на материнской плате, получая самый большой возможный размер модуля памяти для каждого разъема, поскольку это позволит более легко обновить любую будущую память. Например, вы покупаете сервер, который имеет 8 гнезд для подключения ОЗУ с возможностью до 512 Мб для каждого разъема при общем максимуме в 4 Гб. Вы проверили бюджет и может® обосновать, по мнению вашего босса, только 2 Гб, Имеются два разных пути, которыми ваш продавец сервера может выполнить это требование. Более дешевый путь — с 8 микросхемами ОЗУ по Зав Мб каждая. Лучший путь и цена, которую вы хотите представить вашему боссу, — 4 микросхемы ОЗУ по 512 Мб каждая, оставляя четыре пустых разъема. Небольшая переплата, которую вы делаете о этой конфигурацией, будет меньше, чем то, сколько нужно будет уплатить» когда требования ваших данных расширятся, и вы должны будете покупать эти дополнительные 2 Гб. Подсистемы памяти на сервере Windows NT Говоря о 4 Гб ОЗУ, мы имеем в виду некоторые ограничения для ОС, иа которых работает SQL Server. Хотя SQL Server будет работать на системах Windows 9х/МЕ, мы ограничимся только рассмотрением SQL Server на серии Windows NT/ 2000. Важно обратить внимание, что в семействе ОС Windows 9х ОС Windows NT и Windows 2000 имеют различные проекты виртуальной памяти, поэтому алгоритмы, используемые для оптимизации SQL Server, различны, Сервер Windows NT имеет две разновидности Standard edition (базовая версия) и Enterprise edition (версия для предприятий). В то время как обе вервии Windows NT поддерживают максимум 4 Гб ОЗУ, они делят аги 4 Гб по-разному. Standard edition поддерживает максимум 2 Гб памяти, отведенной любому процессу, Эта отведенная память включает и ОЗУ, и виртуальную память, где размещена часть жесткого диска, чтобы действовать подобие ОЗУ, хотя очень, очень медленно. Таким оврааом, даже при том, что вы могли бы иметь 4 Гб ОЗУ на вашем сервере^ процесс обслуживания, который начинается в программе начального запуска ОС, в которой работает SQL Server, будет способен использовать только 2 Гб этой памяти, Другие 2 Гб памяти будут отложены для ОС. Enterprise edition поднимает этот уровень до 3 Г®, обеспечивая по существу ~ 50-процентную надбавку к характеристике. Это сделано с помощью переключателя BOOT.INI. Подсистемы памяти на сервере Windows 2000 Архитектура памяти Windows 2000 немного более сложная. Для начала отметим, что теперь имеются три разновидности ОС: Standard (стандартаая), Advanced (расширенная) и Data Center (информационный центр). Standard во многом додоВиа стандартной версии Windows NT. Она будет поддерживать 4 Гб ОЗУ с максимумом до 2 Гб для любого отдельного процесса, Windows 2000 Advanced повышает объем ОЗУ до 8 Гв. Это обеспечивается двумя различными способами. Первый называется настройкой памяти приложения (Application Memory Tuning) или 4-гигабайтовой настройкой (4-gigabyte-tuning — 4GT). Настройка 40Т очень похожа на то, как NT BnterpriM обращается с памятью. Она исаолмуетея для приложений, которые запускаются на сервере и требуют от 2 до 4 Гб ОЗУ, и имеет максимальное ограничение процесса в 8 Гб.
Чтобы обеспечить полное использование 8 Гб ОЗУ на сервере Windows 2000 Advanced, вы должны вудете использовать расширение физического адреса (Physical Address Extension — РАЕ) Х86. Требования к а п паратным средствам для РАВ Х86 более строгие, адм для 4GT. Чтобы определить, поддерживают и это ваши аппаратные средства, следует проконсультироваться в Window 2000 Hardware Compatibility List (вписок совместимости аппаратных средств Windows 2000) в _ - ' = http:/Awww,mlcrosoft.com^flndows2000Ajpgrade/cofnpaV, разыскивая ключевые слова Large Memory (большая память). Технология Требования к аппаратным средствам Расширение физического адреса (РАН) О Процессор Pentium Pro иди более Х86 Настройка памяти приложения, также поздние □ 4 гигабайта или больше ОЗУ Q 450 NX или совместимый набор микросхем и их поддержка или более поздиин □ Intel-совместимый процессор называемая 4-гигабайтовой настройкой (4GT) □ 2 гигабайта или больше ОЗУ При реализации РАЕ Х86 ова SQL, Server 7 и SQL Server 2000 Enterprise Editions используют Microsoft Windows 2000 Address Windowing ExtenefonB (AWE — адресные оконные расширения), чтобы адресовать приблизительно 8 Гб памяти. В SQL 2000 это относится к одному экземпляру, так как он поддерживает больше чем один экземпляр SQL Server. Однако для каждого экземпляра, использующего эту расширенную память, нугкно разместить его память вручную вместо того, чтобы разрешить SQL Server управлять этим, автоматически иепояьвуя свои динамические программы управления памятью. Windows 2000 Data Center является наиболее амбициозной ОС, которую фирма Mjcroeoft когда-либо поставляла на рынок. В дополнение к поддержке до 32 процессоров, использованию РАЕ Х8в, она может поддерживать 64 Гб ОЗУ. Может выть установлена только SQL 2000 Enterprise Edition, чтобы работать в этом оборудований, Дополнительно сервер ОС Windows 2000 Data Server будет загружать только допустимую конфигурацию аппаратных средств. Допустимые конфигурации аппаратных средств подходят для программы Windows Data Center и поставляются основными продавцами типа Compaq, Dell, Unisys, IBM и Hewlett-Packard. Unisys, например, поставляет на рынок ES7000 с 32-м ЦП и до 64 Гб ОЗУ. Ее дисковая подсистема ввода/вывода поддерживает до 96 каналов. Управление работой памяти Теперь, когда вы знаете ограничения на память в SQL Server, наложенные ОС, что вы можете сделать, чтобы оптимизировать эту память, и определить, нужно ли ее еще? То, чем мы будем сейчас ааиииаться, обычна называется искусствол управления работой.
Обычно мы будем соередотатавать наши усилии на настройке механизмов SQL Server 7 и SQL Server 2000. Эти механизмы имеют-привиийиальаые усовершенствования по ераваетию е SQL Server 6.5 в алгоритмах управления памятью. В отличие от SQL Server 6.5, они динамически выделяют я освобождают память в случав необходимости. Обычно нет необходимости определяй, администратору, сколько памяти следует разместить для серверов SQL Server Т или SQL Server 2000, хотя опция все еще существует и тревуется в некоторых конфигурациях. Из-за динамической природы памяти в SQL Server 7 и SQL Server 2000, фирма Microsoft удалила поддержку для одного из наиболее полезных средств, доступного в SQL Server 6,5 — DBCC MEMESAGE, Теперь, чтобы получить эту информацию, вы должны контролировать разнообразные измерители характеристик в объекте Buffer Manager (менеджер буфера) и объекте Cach Manager (менеджер кэша), так как информация больше не отображается статически в таблице, наподобие sysconf igvres (системные конфигурации). Хотя этот удобный инструмент теперь внесен в список как “Не поддерживаемый" и больше ве возвращает разбиение использования памяти, попытайтесь запустить его. Вы увидите, что-aw» "выстрел навскидку" продолжает возвращать верхние 2D элементов сливка буферированных таблиц и индексов. Это может быть очень удобный список. При авали» характеристик конкретного приложения в испытательной конфигурации это может выть неоценимо. Искусство управления работой Управление работой является искусством, н в этом-смысле является комбинацией таланта, опыта, знания, и иногда только простой удачей, Как вы думаете, можете ли вы это сделать? Вы должны пробовать, пробовать, и снова пробовать. Настойчиво лелже это, специально изучайте это. Держите монитор постоянно включенным для обслуживания ваииго сервера. Имеются некоторые хорошие руководящее принципы для качала. 1. Удостоверьтесь, что вы запустили ваш типичный процесс (SQL Server) и загрузили его работой (запросы и хранимые процедуры) а течение вашего управления. 2. Только не осуществляйте управление вашими серверами s реальном масштабе - времени. Выделите долго протекающие загрузки. В Windows NT установите сервис Datalog/Monitor из NT Resource Kit; эта функциональные возможности являются новыми в Windows 2000. 3. Всегда держите счетчики диска включенными, запускаемы?,-и ат команды, подтверждающей команду DXSKPERF — Т.н затем перезагружайте их. Когда в производственном оборудовании непроизводительные издержки минимальны, последнее, что вам нужно сделать в середине коллизии, когда необходимы счетчики логических и физических дисков — необходимо их перегрузить. 4. Для ежедневного контроля программы на рабочем столе выводите окно е диаграммой е интервалом в 18 секунд. И в Windows NT PerfMen, и в Windows 2000 ММС SysMon, это даст пи&ио в течение 30 минут. По моему мнёнйю, это является идеальным интервалом и с точки зрения наблюдения прошлого, и с точки зрения умеиыиеяия воздействия не сервер. 5. Используйте SQL Profiler для отдельных запросов и процессов в сочетании с PerfMon или SyaMon, чтобы получить хорошую картину влияния отдельных запросов.
6. Знайте терминологию управления работой. Объекты —это списки конкретной доступной статистики. Примером может быть объект Processor (процессор). Счетчик — отдельная статистика, которая находится под управлением объекта. Примером является счетчик % Processor Time (процент времени, отведенного процессу) в объекте Processor, Экземпляр — дальнейшее разбиение статистики счетчиков на отдельные компоненты. Не все счетчики будут нметьотдельные экземпляры. Счетчик % Processor Time’ имеет экземпляры для каждого - процессора, а экземпляр a_Total (общее время) содержит суммарную активность всех процессоров. 7. Знайте вятпи средства. Хотя вы можете и знать, как установить диаграмму в PerfMon, изучите, как задать загрузку с Datalog или Performance Log. Другие средства, с которыми нужно познако.митьея — DBCC MEMUSAGB, Task Manager и SQL Enterprise Manager Current Activity. 8. He бойтесь экспериментировать, Baek Office Resource Kit имеет средства для организации проверки данные (DataSim), создания иенытательши. ВД (DBGen) и моделирования загрузки от многих клиентов (SqILSj. Управление работай SQL и узкие места Узкие места возникают, когда ресурсы аппаратных средств не могут поддерживать требования программного обеспечения. Например, когда процесс программного обеспечения или комбинация процессов требуют больший объем операций ввода/выводв с диска, чем диск может физически обеспечить, на диске возникает узкое место. Когда подсистема ЦП становится слишком насыщенной и процессы ожидают своей очереди, возникает узкое место. Узкие места обычно фиксируются одним из двух способов. Первый должен определить ограничивающие аппаратные средства и увеличить; их способности, Другими словами, возьмите более быстрый-накопитель на жестких дисках или узеличьте скорость ЦП, Второй путь состоит в том, чтобы вынудить процессы программного обеспечения использовать аппаратные средства более эффективно. Это могло бы быть сделано, помещая индекс таблицы так, чтобы или необходимость ввода/вывода с диска для. обслуживания запроса была уменыпена, или число элементов ЦП, необходи?.‘ых для обработки объединения, уменьшилось. Следующие пять ключевых областей следует контролировать при прослеживании работы сервера и определении узких мест. Каждый кандидат на узкое место будет изменять работу рассматриваемых контролируемых объектов и счетчйков. □ Использование памяти — SQL Server нуждается в памяти и для себя, и для ОС. Если SQLServer имеет дветаточяо памяти, но ОС испытывает недостаток памяти так. что она вынуждена часто передач иьагь страницы файла на даек, вся работа будет существенно страдать от этого, . =: . . - - - □ - Использование ЦП — Большая загрузка ЦП указывает, что подсистемаобладает недостаточно;"! мощностью. Решением могли бы быть модернизация ЦП или увеличение числа процессоров. □ Организация операций ввода/вывода о диском —’ Неспособность диска вли контроллера диена удовлетворять требованиям чтения или запаси надлежащим образом сильно воздействует на функционирование. □ Соединения пользователя — Ненадлежащим образом задание ряда соединенна может отобрать память, в противном случае доступную для SQL Server.
□ Блокировки — Один процесс препятствует другому процессу вызывать или обновлять данные. Это особенно важно для пользователей, и может выть причиной некоторых из ваших наиболее серьезных проблем функционирования с точки зрения пользователя. Настройка памяти: ОС и SQL Server Начните ваш интенсивный анализ памяти, рассматривая два противоположных счетчика: □ Memory: Available Bytes (память; доступные байты); Q Memory; Pages Faults/sec (память: число ошибок в секунду, связанных с отсутствием страниц). Счетчик Available Bytes сообщает, сколько памяти доступио для использования процессами. Счетчшс Pages Faults/sec еоо(мцает вам число ошибок обращения к страницам на жестком диске, которые должны быть восстановлены с жесткого диска, таи как они не выли в рабочей памяти. Он также включает число страниц, записываемых на жесткий диск, чтобы освободить место в рабочей области дай поддержания ошибок страниц на жестком диске. Маасе число Available Bytes указывает, что может не быть достаточной доступной памяти или что тгроиевды, включенные в SQL Server, не могут освободить память. Большое число Pages Faalte/sec указывает на чрезмерную страничную подкачку информации. Рассматривая глубже отдельные экземпляры Process: Page Faults/sec, можно сделать заключение, что процесс SQL Server, например, имеет чрезмерное разбиение на страницы. Низкая скорость Pages Faults/sec (обычно 5*10 в секунду) нормальна, поскольку ОС продолжит делать свое дало, выполняя набор работ. Как раньше выло отмечено, начиняя с SQL Server Т, память по умолчанию настраивается автоматически. Тем не менее, обычно вы хотите дать SQL столько выделенной памяти, сколько возможно. Это главным образом зависит того, как другие приложения могут запускаться на сервере. Используя хранимую процедуру sp_conf igure (хранимая процедура конфигурирования), вы можете устанян-ивать нужные значения в MIN SERVER MEMORY (минимальная память сервера) и MAX SERVER MEMORY (максимальияя память сервера). Если SQL Server — единственное приложение на сервере, валяйте параметрам MIN SERVER MEMORY и MX SERVER MEMORY одно и то же значение. Если SQL Server сосуществует с одним или большим количеством приложений, уменьшайте MIN SERVER MEMORY до значений, обеспечивающих требования других приложений. Если другое приложение не в состоянии начать работать должным обрезом, это может произойти потому, что ВОД* Server вынужден работать вблизи установки MAX SERVER MEMORY и медленно освобождает память для нового приложения, испытывающего недостаток памяти. В этом случав понизьте значение мах SERVER MEMORY. Очевидно, значение ИЛХ SERVER MEMORY всегда должно быть больше или равняться MIN SERVER MEMORY. .
Если вы установили и запускаете поддержку Full-Text Search — полнотекстовый поиск (Microsoft Search sendee — сервис поиска фирмы Microsoft — также известный как MSSearch), то вы должны установить опщпо MAX SERVER MEMORY, оставив достаточно памяти для запуска сервиса MSSeareh. Microsoft дает здесь удобную формулу: полная виртуальная память — (максимальная память для SQL Server + виртуальная память для другая процессов) = 1.5 * физическая память сервера. Как только мы настроили память SQL Server, хорошо решить, хотите ли вы, чтобы SQL Server 7/2000 настраивал память процессов автоматически или имел набор вяачений для конфигурации. Для лучшего функционирования можно заблокировать количество рабочей памяти, которую зарезервировал SQL Server. Компромисс здесь заключается в том, что можно получить сообщение "недостаток памяти" для других приложений на том же самом сервере. Если вы решаете зафиксировать количество рабочей памяти, необходимы две настройки конфигурации. Сначала уравняйте настройки HIM SERVER MEMORY и MAX SERVER MEMORY. Затем включите флаг конфигурации SET WORKING SET SIZE (включение установки размера), использующий sp_configure. Значение MAX SERVER. MEMORY не должно вообще превышать ОЗУ, доступного для сервера. Динамическая настройка памяти в SQL Server 2000 Так как же работает динамическая настройка? Приведенный ниже кадр экрана показывает настройки свойств памяти для экземпляра SQL Server 2000, Данвыи экземпляр называется GC14HS801,- Это означает сервер CD1 и второй или следующий из экземпляров, так как первый яадается по умолчанию просто именем сервера. Если посмотреть глубже, экземпляр запускается с процессом, идентифицированным как MSSQL$InstanceName, в данжом случае, KSSQLSMSBOl. Экземпляр по умолчанию, как в SQL Server в.5, так и в SQL Server 7.0, имеет имя процесса MSSQLServer. Экземпляр CD1XMS8D1 имеет настройку минимума, равную 60, и максимума, равную 512, который является полным ОЗУ, установленным на этом конкретном сервере. С этими настройками вы считаете, что когда запускаете экземпляр CD1 \MS8D1, сразу требуется 60 Мв ОЗУ, и ом может потенциально использовать все 512 Мб. Это не является работой с динамической памятью. Когда вы первый раз запускаете экземпляр на SQL Server 2000, первоначально требуется 8+12 Мб ОЗУ. (ta получает большее количество ОЗУ для своего процесса только тогда, когда подключаются пользователи и увеличивается рабочая нагрузка. В случае CDXWS8D1 он постепенно увеличат свою область памяти, назначенную процессу MSSfiMMSBDl. Как только память достигает сконфигурированного порога значения » 60 Мб, ©иа не будет опускаться ниже этой уставки, В то же самое время, когда рабочая нагрузка увеличивается, экземпляр будет постепенно подучать память. Память, однако, не достигнет 512 Мв — значения, установленного ОЗУ яа сервере. Вместо этого, фирма Microsoft, разработала ряд алгоритмов, которые добавляют память. Эти алгоритмы имеют две коикретвые цели: □ обеспечить свободную память ОС s пределах 4г10 Мб; □ распределить память между экземплярами SQLServer с учетом нх отиосителькых рабочих нагрузок.
Целевая свободная память Как было раньше отмечено, установка максимума для экземпляра СИЛМЗВЩ в- вышеутюмянутом кадре экрана была 512 -Мб — значение установленного ОВУ ня сервере. Эксперимент»: Microsoft показали, что поддержанием 4 до 10 Мб памяти на сервере Windows минимизирует обмен страниц файда. Конкретное значение, катеров SQL Server ищет, чтобы обеспечить значение в этом диапазоне 4-10 Мб, называется целевой-свободной памятью. Алгоритмы выли разработаны так, что когда SQL Server потребляет все больше памяти для кэша данных, количество свободной памяти для ОС уменьшается. Когда достигнут порог 4-10 Мб, SQL Server прекратит добавление памяти. Целевая свободная память — около 10 Мб для слабо- загруженного SQL Server и постепенно уменьшается до 4 Мб для сильно загруженного SQL Server, Эта целевая свободная память — отражение ожидаемой жизни страницы в буфере-накопителе SQL Serrtr. Когда сервер сильно загружен, страницы более часто загружаются я кэш буфера, а старые страницы удаляются, уменьшая продолжительность жизни стран инь:, Когда продолжительность жизни страницы уме.иынаетея, SQL Server поддерживает свободную память ОС- у нижнего края (4 Мб) даапазояа 4-10 Мб. Когда сервер менее занят и гфсдолжительностъ жизни страницы возрастает, он поддерживает целевую свободную память ближе к 1Q Мб. Таким образом, возвращаясь к нашему экземпляру GDI \MS 8 D1, максимальная память никогда не будет приближаться к 512 Мб ОЗУ. Скорее, это, вероятно,- будет где-нибудь в диапазоне от 460 до 470 Мб, так как сама ОС забирает 3040 Мб 08У, и целевая свободная память в 4-10 Мб ОЗУ всегда поддерживается свободной для ОС, чтобы избежать страничную подкачку.
Так что происходат,..когда вы «впускаете SQL Server, и он и одерживает целевую свободную память 4+10 Мб ОЗУ, доступную для ОС, и вы внезапно начинаете большой процесс, который миимает 20+25 Мб ОЗУ? Оказывается, что SQL Server динамически уменьшит свою память-в кеше данных, чтобы обеспечить -1+10 Мб свободной памяти ОС. Ок может делать это очень быстро, порядка нескольких Мв в секунду. Настройка памяти для нескольких экземпляров Когда SQL Server должен разделить ОС с другим процессом, например а сервиоом -Mferoeoft Search, максимальная память SQL Server должна быть сконфигурирована вручную. Как вы, вероятно, теперь уже знаете, SQL 2000 позволяет реализовать несколько экземпляров SQL Server на одной и «я же ОС. Это верно, независимо от того, используется ли ОС Windows NT пли Windows 2000, Как же осуществляется работа в ситуации, когда два или более экземпляров SQL Server установлены на одном и том же сервере? В таком случае SQL Server имеет достаточно средств, чтобы балансировать нагрузки между этими двумя экземплярами. Вспомните из предыдущего раздела экзежа®ф<УМЛМБ801. Очевидно, экземпляр цр умолчанию называется CD1, Мы рассмотрим два таких экземпляра, но теория будет той же самой для трех или белее экземпляров. Сколько экземпляров вы можете запускать на отдельном сервере? Microsoft рекомендует один — экземпляр, используемый по умолчанию, для работающего сервера. Известно об отдельных испытаниях, успешно управляющих 10 и 16 экземплярами. Мы, конечно, не рекомендуем больше 10 экземпляров. Вообще, вы можете захотеть запустить несколько экземпляров на одном сервере, чтобы поддержать, например, формирование конфигурации на одном или нескольких экземплярах и обеспечение качества или проверку оборудования на другом экземпляре. Все это, конечно, возможно при полных характеристиках аппаратных средств используемых экземпляров. При запуске системы, каждый иа экземпляров, CD1 и CDlVMSaDl, немедленно получит 8+12 Мб ОЗУ. Они останутся иа этом уровне, пока соедннения пользователя и рабочие нагрузки яе начнут увеличиваться на одном или обоих экземплярах. Тогда они будут получать память независимо друг от друга, пока ОС не станет иметь от 4 до 10 Мб свободной доступной- памяти, как отмечено в предыдущем разделе. В этот момент они начинают конкурировать друг с другом за иетользояажйе памяти иа основе своих значений целевой свободной памяти. Кан пример, если экземпляр по умолчанию имеет большую рабочую магрувяу, его целевая свободная память будет около 4 Мб» Пуст* экземплярCftlMMseDL менее загружен и имеет целевую етободиую память около 10 Мб. Предположим, что только 7 Мб свободной памяти доступно для ОС. Как вы и ожидали бы, сильно загруженный экземпляр CD1 со значением 4 Мб продолжит получения памяти. Так как мало загруженный СМ WS8©!» яе видит свободных 10 Мб, он будет освобождать память ОС, пытаясь--доетигауть ве целевого значения свободных 10 Мб. Не думайте относительно того, сколько памяти используется в каждом процессе. Лучше думайте атяосительво-- продолжительности жизни страницы и зиачеетя соответствующей целевой свобЕдаяй памяти для этого экземпляра.
Таким образом формируется контур отрицательной обратной сняли, чтобы регулировать количество памяти, которую каждый экземпляр получает в зависимости от его рабочей нагрузки. Так как CD1, сильно нагруженный экземпляр, получает большее количество памяти, ом может сохранять страницы в памяти дольше, увеличивая их продолжительность жизни. Поскольку продолжительность жизни страницы увеличивается, значение целевой свободной памяти, которое было равно 4 Мб, увеличивается. На слабо загруженном экземпляре CD1 \MS8D1 память освобождена, таи как SQLServer пытался увеличить целевую свободную память ОС до 10 Мб. Когда память освобождена, страницы стасраийЮтен в памяти в течение более короткого периода, уменьшая свою продолжительность жизни. Поскольку продаякительность жизни страницы уменьшается, значение целевой свободной памяти удаляется от 10 Мб. В некоторый момент слабо нагруженный экземпляр освободит достаточно памяти (по крайней мере, до ее минимального значения) так, что значения целевой свободной памяти иа этих двух экземпляров сравняются. Камов заключительный результат? Значения целевой евовощюй памяти (и соответствующая продолжительность жизни страницы) этих двух экземпляров сравняются. Сильно загруженный экземпляр все еще сильно загружается. Креев того, учитывая теперь приблизительно 460 МВ СИУ, даетупиых всем экземплярам SQL Server, сильно загруженный экземпляр будет иметь, например, 480 МВ ОЗУ, выделенного для него. Слабо загруженный экземпляр будет иметь около 100 Мб ОЗУ, Равновесие продолжается, пока относителыйде рабочие нагрузки экземпляров остаются неизменными. Как только рабочая нагрузка на одном или другом экземпляре увеличится ид» уменьшится, продолжительность жизни страницы и соответствующая целевая свободная память отреагируют и переведут экземпляры в новое состояние равновесия. Настройка памяти процесса в SQL Server Как только вы получили полностью настроенную память ОС и SQL Server, поподробнее рассмотрим использование памяти в SQL Server. Здесь желательно использовать четыре счетчика: Q Process: Working Set: sglservr (процесс: количество рабочей памяти: SQL Server); □ SQL Server: Buffer Manager: Buffer- Cache Hit Ratio (SQL Server: диспетчер буфера: результативность каша буфера); □ SQL Server: Buffer Manager: Free Buffers (SQL Server: диспетчер буфера: свободные буферы); Q SQL Server: Memory Manager: Total Server Memory (KB) (SQLServer: диспетчер памяти: полная память сервера-(Кв)). Экземпляр Working Set: sglservr показывает количество памяти, которое использует SQL Server. Воли его число постоянно ниже, чем количество, сконфигурированное для использования в SQL Server с помощью опций MIN SERVER MEMORY и MAX SERVER MEMORY, то SQL Server сконфигурирован для слишком большой памяти. В противном случае вам нужно увеличить ОЗУ и MAX SERVER MEMORY. Значение Buffer Cache Hit Ratio должно быть постоянно больше 90. Это уиммвает, что кэш данных обеспечивает 90 процентов от запросов данных, Если данное значение постоянно низкое, это является очень хорошим индикатором, что для SQL Server необходимо большее количество памяти. Воли Available Bytes (даступвые байты) мало, это означает, что мы должны добавить большее количество ОЗУ.
Когда значение Free Buffer мало, это означает, что нет достаточной оперативной памяти, чтобы обеспечить постоянную величину ката данных. Это также указывает, что нужно больше памяти. Если значение Total Server Memory для SQL Server постоянно больше, чем полная память сервера, это указывает, что нет достаточного ОЗУ. Настройка работы сервера Если этот сервер предназначен для SQL Server или если SQL Server считается наиболее важным приложением ва машине, имеется возможность изменения конфигурации, которое вы при желании можете выполнить, чтобы увеличить производитс-льность. Вы можете управлять относительными количествами процессорного времени, которые будут предоставляться высокоприоритетным приложениям по сравнению е фоновыми приложениями. Значение по умолчанию для Windows NT должно дать высокоприоритетным приложениям самый высокий приоритет и, следовательно, лучшее время отклика. Чтобы сконфигурировать эти настройки, откройте Control Panel (панель управления) из меню панки Settings (настройка) меню Start (нуск). Откройте апплет System (система) в Control Рапе! я выберите страницу Performance (выполнение), как- в кадре экрана, - приведенном ниже. Используйте управляющий движок на этой странице для регулировки высоких и фоивяых приоритетов. В Windows NT Workstation планировщик ОС будет всегда давать фоновые потоки в два временных тика. Временные тики могут равняться MJ мс в зависимости от Hardware Abstraction Layer (HAL — уровень апп ярати ых абстракций). В зависимости от положения движка высокоирврритетные приложения получат 2, 4 или в временных тиков, соответствующих значениям от None (нет) до Maximum (максимум). Небольшой известный факт — в сервере Windows NT это не имеет абсолютно никакого воздействия. Изображение графического интерфейса работает, но от этого ничего не изменяется. Все процессы получают период в 12 временных тиков, -
В отличие от сервера Windows NT, сервер Window» 2000 действительно работает. Но это несколько более просто. Ив варианте сервера, ж в варианте рабочей станции, называемом Windows 2000 Professional, вы получаете диалоговое окно, показанное на следующем кадре экрана, щелкнув кнопкой Performance Option (опции выполнения) на странице Advanced .(расширенный) апплета Control Panel | System (панель управления | система). Когда вы оптимизируете работу для варианта Applications в Windows 2000, то получаете отношение высотощжчитетиото потока к фоновому потоку как 2 временных тика к в тикам, которые рабочая станция Windows NT использует, :когда она оптимизирована для высокоприоритетных приложений. Когда вывыС^аете Background eervicee для Windows 2000, то получаете режим сервера Windows NT — 12 зременных тиков дэшвсех потоков. Рекомендуется выбирать Background services на всех серверах Windows MW, запускающих SQL Server, чтобы обеспечить одинаковое процессорное время как для высокоприоритетных, так и для фоновых приложений. Это дает сервисам Microsoft SQL Server больший процент производительности, так как они почти всегда управляются как фоновые сервисы. Подсистемы ЦП Подсистема ЦП — сердце и дш вяпюго серверя. Будучи таковой, она является основным кандидатом на настройку функционирования. В недавние годы превалировала симметричная мультипроцессорная (symmetric multi-procissur SMP) конфигурация. Сервер Windows NT поддерживает четыре новых процессора, не имеющих специ ал ьиых механизмов уровня аппаратных абстракций (hardware abstraction layers —• НАЦ. Сервер Windows 2000 обеспечивает поддержку четырех процессоров» в то аремя ?:ак сервер Windows 2000 Advanced поднимает эту планку до восьми процессоров. Сервер Windows 20С0 Data Center далына увеличивает значение до 32 процессоров. Вы можете производить иа этом уровне существенные вычисления. Что вам следует использовать — Windows 2000 или Windows NT? Определенно, Windows 2000. Для. четьфехпроцессорного сервера Windows 2000 дает на 68 процентов улучшение работы по сравнению с подабвым сервером Windows NT. Windows NT даже не поддерживает что-либо более четырех процессоров, за исключением специализированной аппаратуры. Windows 2000 также поддерживает родственность процессоров. Это похоже на то, когда процесс типа SQL Server устаиовлви на одаож или нескольких процессорах. Это аффективно заменяет. мулмвроцееоорньЛ параллелизм, которыж-выл переписан для SQL Server как опция конфигурации после SQL 6,5. Пожалуйста, обратите внимание также, что как и в SQL 6.Б с мультипроцессорным параллелизмом, вы должны быть осторожны при использовании родственности процессоров, так как are будет часто связано с хранением процесса типа SQL Server ва мен« используемом процессоре.
В настоящее время появляется экономически эффективное "свежее пятно” на уровне четырех процессоров. Так как вы должны конфигурировать SQL Server, чтобы максимизировать использование ЦП? Для SQL Server 7 и SQL Server 2000, по-видимому, потребуется пассивное участие! Хотя некоторые аргументы и могут быть сказаны в пользу настройки, подсистемы ЦП в SQL Server 6.5, системы SQL Server 7 и SQL Server 2000 хорошо настроены, чтобы обеспечить максимальное преимущество любой организации ЦП, которую вы используете. Контроль работы ЦП При организации контроля работы ЦП мы собираемся использовать несколько счетчиков: □ Processor: % Processor Time (процессор: процент процессорного времени); □ Processor: % Privileged Time (процессор: процент привилегированного времени); □ Processor: -% User Time (процессор: процент пользовательского времени^ □ System: 1 Total Processor Time (система: процент полного процессорного времени). Обычно контроль работы ЦП осуществляется непосредственно. Вы должны начать с контроля Processor: s Processor Time. Если вы имеете более одного процессора, то должны контролировать каждый экземпляр этого счетчика, а также контролировать System.: % Total Processor Time, чтобы определить среднее значение для всех процессоров. Постоянные скорости использования более 8(МЮ процентов могут указывать на плохую настройку или проектирование приложения. С другой стороны, если вы использовали все другие рекомендации этой книги, то это может указывать на потребность в более мощной подсистеме ЦП. Вообще говоря, я потратил бы немного времени для анализа приложений перед тем, как немедленно пойти и купить еще три процессора. Потратив это время на экспериментирование, чтобы определить проблемы в работе ЦП, и устранение их через усовершенствования программного обеспечения часто оградит вас от расхода денег на более мощный ЦП, который только "закроет" плохо написанное программное обеспечение в течение маленького или никакого периода, времени. Если вы наблюдаете интенсивное использование ЦП, то далее захотите проконтролировать Processor: % Privileged Time. Это — потраченное время на операции уровня ядра, типа дасковых операций ввода/вывода. Если этот счетчик постоянно имеет скорость использования более 9МЯ процентов и соответствует большим значениям счетчика работы даска, скорее всего, это связано с узким местом диска, а ие ЦП. А что касается SQL Server? Счетчик Processor : % User Time измеряет величину процессорного времени, используемого приложениями, не относящимся к уровню ядра. SQL — имевво такое приложение. Если эта величина велика, и имеется невкольно процессов, запущениых иа сервере, вы можете захотеть посмотреть глубже на конкретные экземпляры процессов с помощью вкземпляров счетчика Processor: i Ожёг! Time, Это : может быть очень полезно для случаев типа того, когда наши инженеры ОС установили новое антивирусное программное обеспечение на всех наших серверах. Это временно принесло бы им удовлетворение, пока мы не можем Шфеделить виновника с помощью аяализа Processor: % User Time для экземпляра антивирусного программного обеспечения. 20-11» 575
Волоконное управление На языке использования ЦП потоки — единственные процессы, управляемые процессором. Потоки управляются ОС в режиме ядра. SQL Server может поддерживать до 255 открытых потоков, каждый иа которых используется дак обработк и пакета. С появлением SQL Server 7 был введен новый термин волокна (fibers). В то время как потоки обрабатываются ОС в режиме ядра, волокна оврабатывиотся режиме пользователя самим приложением. В OLTP-обрудовании высокой производительности число пакетов и, следовательно, потоков, переданное подсистеме ЦП, может быть очень велико, обычно приближаясь к сконфигурированным по умолчанию 255 потокам. Каждый раз, когда контекст потока . переключается на ЦП, ЦП должен выйти из режима пользователя, перейти через переключатель контекста в режим ядра, взять новый поток и затем переключить контекст назад режим пользователя. Для ЦП переключатель контекста относительно дорог. Используя волокна, SQL Server будет порождать несколько иноков и затем несколько волокон к каждому потоку. Волоконное управление связано с кодом уровня приложения на SQL Server, а не ядра ОС. Так как волокна запускаются режиме пользователя, когда ЦП переключается с одаого волоки» иа другое в одном и том же потоке, то для ЦП нет переключателей контекста. Два цикла ЦП — один, когда цц захватывает новый поток, далее через переключатель контекста попадает в режим ядра и затем возвращается обратно, и даутой, когда ЦП использует новое волокно, не требующее перечисленных операций, отличаются по времени на порядок. Волокна годятся не дая каждой ситуации. Они ограничены единственным потоком, который всегда назначается единственному ЦП. Таким образом, когда поток процесса может обслуживаться одним ЦП, а затем другим, волокно процесса будет ограничено единственным ЦП. OLTP-приложения «деляются особо, так как они обычно имеют очень короткие, определенные транзакции, запускаемые в процессе, «держащемся в тадоте или волокне. Используйте алепукмиий контрольный список, чтобы определить, могут ли тжна быть нами испол-ыюняны: 1. Ваш сервер должен быть полиостыо связан в исиоамоааиввм SQL Server н обычно применяется %пя OLTP-оборудования. 2. Вы имеете очень высокую скорость использования ЦП (больше 90 процентов). 3. Вы наблюдаете чрезмерно высокую скорость переключений контекста, используя счетчик System; Context Swltehes/sec (система: число переключений контекста в секунду). Если эти параметры соответствуют вашей система, то можете попробовать поэкспериментирозать с использованием волокон, переключая настройку конфигурации "облегченного пула" от 0 до 1. Дисковые подсистемы Вторая наиболее важная характеристика системы, определяющая хорошее функционирование вашего приложения на SQL Server, — дисковая подсистема. Помните, что функционирование главным образом определяется максимизацией числа чтений данных на быстродействующей памяти. Обмен этими данными с быстродействующей памятью и хранение их — ответственность дисковой подсистемы.
Основная дисковая подсистема Основваж дисковая подсистема включает жесткий диск и контроллер жесткого диска, вваимадяйствующий с материнской платой. Жесткие диски имеют различные эксплуатационные характеристики. Мы можем рассматривать их с точки зрения чтения и записи, с точки зрения чтения данных с диска последовательно или случайным образом и с точки зрения скорости вращения диска. ' Характеристики чтения, естественно, отличаются от характеристик записи на диск. Как вы могли бы ожидать, запись относительно медленнее по сравнению с чтением. Таким образом, мы хотим минимизировать время ожидания при записи на диск. SQL Server, естественно, оптимизирует это, используя комбинацию потоков конкретного рабочего процессора и систему отложенных записей. Эти две системы объединяются для просмотра кэша буфера в активной памяти и уменьшения счетчика, указывающего относительную занятость страницы в кэше вуфера. Обращение к странице вызывает увеличение его счетчика. Таким образом, частое обращение к страницам сохраняет их к кэше буфера. Если счетчик страницы, расположенно! в кэше буфера, достигает нуля, и флаг изменения страницы установлен, поток конкретного рабочего процессора или, что менее часто, система отложенных записей запланирует сбрасывание страницы на диск. Ключевой момент здесь заключается в том, что это делается асинхронно с операциями чтения в потоке, так что время ожидания записи минимизируется. Чтение данных с диска — это та операция, где можно получить наибольший выигрыш в характеристиках. Намного более эффективно читать с диска данные последовательно (нежели беспорядочным образом). Таким образом, использование кластерных индексов для загрузки большого объема данных в кэш данных для анализа всегда оптимально. Наконец, если вы можете выбирать между дисками, которые имеют скорость вращения 7200 оборотов в минуту в 15000 оборотов в минуту, естественно выбрать последний. Диски, которые вращаются быстрее, получают данные под головкой чтения быстрее и помещают их в кэш данных быстрее. Конечно, они более дорогие. Одиако вы можете иметь самый быстрый жесткий диск в мире и тем не менее иметь слабую исполнительную систему, если у вас нет хорошего контроллера, связывающего жесткий диск и материнскую плату. Работа контроллера характеризуется пропускной способностью в битах. 64 бита, очевидно, лучше, чем 32. Что еще важног дли контроллеров, так это их способность кэширования. Современные контроллеры обладают как кэшированием опережающего чтения, так и кэшированием опережающей записи или обратной записи. Когда контроллер читает в опережающем режиме, он использует сложные алгоритмы, чтобы дождаться потребности в данных, и размещает эти данные в быстродействующем кэше, пока центральный процессор не будет готов их принять. Кэширование опережающей записи или обратной вынуждает контроллер посылать данные в быстродействующий кэш и возвратить в ОС статус завершения. Затеи данные фактически записываются на диск контроллером независимо от ОС. Важно, что способность кэширования определяется объемом сверхбыстрого ОЗУ, используемого джя кэширования этой информации. Снова, чем больше, тем лучше, ио также и дороже. Кэширование записи и SQL Server Многие контроллеры обладают особенностью, называемой кэшированием записи (Write Caching). Это когда диспетчер получает данные от ОС и по существу сообщает ЭС, что данные записаны на диск, хотя фактически они помещены в высокоскоростное ОЗУ для возможной записи на диск.
Это может быть высокопроблематично. Все схемы обеспечения целости ости данных SQL Server в случае неправильной работы сервера зависят от загрузок трян.зякпий. записанных на диск да того, как основные таблицы будут фактически изменены на диске. С кэширующим записи контроллером SQL Server может ошибочно сообщить, что загрузка транзакции надежно находится на жестком диске, когда фактически она все еще находится в высокоскоростном ОЗУ кэшированного контроллера. Если в этот момент работа сервера нарушается, целостность SQL при восстановлении совершенных транзакций и возвращении запорченных транзакций потенциально будет нарушена. Большинство средних и высококачественных серверов сегодня имеют контроллеры с кэшированием записей и резервной батареей на плате, используемой только для контроллеров и их дисковых матриц. При на-шчии этой резервной батареи вы должны бынь осторожны при включении контроллеров с кэшированием записи. Однако, что произойдет, если батарея испортится? Инженеры сервера посоветуют вам не волноваться. Они получат сообщение alarms/’paged (тревога/оповещение). Одвш® из нашего личного щпдтедаже с очень компетентным штатом инженеров сервера в высококачественным сервером от одного из ведущих и наиболее известных изготовитией серверов, мы только могли выяснить, что первый из двух резервных источников энергии для сервера отказал, когда второй также отказал и сбил весь сервер. Даже □ри том, что оповещение было включено, по некоторым причинам это не сработало. Короче говоря, за очень, очень немногими исключениями Microsoft почти всегда рекомендует, чтобы кэширование записи у контроллеров, через которые SQL пишет информацию на диск, было выключено. Исходя из нашего опыта, мы целиком с втим соглашаемся. За исключением очень небольшого числа OLTP-приложений это маленькое преимущество выполнения полностью возмещается дополнительными часами сна, которые получит ваш администратор базы данных. Задумайтесь об этом. Вспомните, как первый рае (и надеемся единственный) ды печатали десять страниц курсовой работы в текстовом процессоре без частого сохранения, и при этом мигала световая сигнализация? Если вы собираетесь включить кэширование записи, то действительно должны иметь очень верьеаиую причииу для этого и очень высокое доверие к аппаратным средствам, инженерам и системам, поддерживающим и контролирующим их. Подсистема RAID Диски и контроллеры могут- быть соединены вместе в матрицу недорогих устройств е избыточностью (Redundant Array of Inexpensive Disk — RAID). Основная цель таких комбинаций состоит в том, чтобы обеспечить устойчивость к ошибкам (другими словами избыточность), такую, что отказ любого компонента не будет сбивать всю систему. Комбинации RAID могут быть или про граммно-организован ы, когда ОС Windows NT или Windows 2000 обслуживает матрицу, или аппаратао-организованы, когда специализированный контроллер обслуживает конфигурацию RAID. Вообще имеется три плюс один путь рассмотрения конфигурации RAID. Эта три пути называются RAID О, RAID 1 и RAID 5. Дополнительный называется или RAID 10 или BAID 0+1. RAID 0, RAID 1 и RAID 5 могут быть как программно-, так и аттяратио-организованы. RAID 10 имеет только аппаратное решение. raid о RAID 0 — вто когда данные разделены на дисках на блоки, обычно размером около 64 Кб каждый. Единственный контроллер или ОС записывают участки блоками в 64 Кб на один диен, а затем продолжает писать бдок на следующий даек. Запись и чтение таких матриц могут быть чрезвычайно быстрыми. Чтение и запись оказываются быстрыми, потому что имеется много головок, которые могут читать блоки данных одновременно.
Многие скажут вам, что RAID О не является на самом деле структурой RAID, так как она не избыточна. Мы лично согласны. Если любой иа даосов отказывает, то мша система со скрипом остановится, и вы можете (удтительно!) потерять данные. Имеется преимущество RAID 0 в смысле скорости. Так как никакая дублирующая информация ие размещается на каком-либо другом диске, данные пишутся быстро участками или Алоками данных иа каждый диск. Точно так же и при чтении (и конкретном последовательном чтении) работа может быть чрезвычайно хороша. В дополнение к скорости есть другое преимущество конфигурации RAID 0 — то, что она может поддерживать очень большие массивы. Даже при том, что это на самом деле не "избыточная" матрица, важно детализировать ее, так как ее характеристики означают, что она представляет одну из двух конфигураций RAID, которые будут использованы позже, когда мы будем обсуждать RAID 10. Следующей является RAID 1. RAID 1 RAID 1 называете* зеркальным отображением, когда одан контроллер зеркально отображает два диска, или дублированием, когда каждый из даух контроллеров управляет отдельным диском, чтобы поддерживать зеркальные копии данных. Часто термин "зеркальное отображение" используется и тогда, когда термин "дублирование" был бы более технически правилен. Обычно достаточно знать, что упоминаются идентичные подсистемы. По существу, задаются два идентичных тома диска — А и В. Когда данные записываются на А, они одновременно записываются и на В. Тома находятся в вечной синхронизации. Когда А или В отказывает, дисковая подсистема не теряет работоспособность. В этом случае возникают (будем надеяться) все виды аварийных сигналов, чтобы сообщить вам, что больше нет избыточности. Чтобы все восстановить, вы должны "разорвать зеркальное отображение", воспользоваться новым двигателем и заново синхронизировать зеркальное отображение. Большинство подсистем, которые поддерживают зеркальное отображение, позволяют вам сделать это без какой-либо остановки сервера. Что пря этом страдает? Помните "I" в аббревиатуре RAID? Она означает "недорогой". В случае RAID 1 вы должны купить два байта хранения для каждого байта лянных. В дополнение к затратам неудобством ’ конфигурации RAID 1 является то, что она может быть только равмером в един, жесткий диск. .
RAID 5 RAID 5 — наиболее общая конфигурация RAID и подобно RAID 0 данные записываются на ряд дисков в виде блоков. Однако в то время век RAID О требует только два диска, RAID 5 требует по крайней мере три или большее количество дисков. Каждый удееток, тем ие менее, будет теперь иметь пуб.тирукнпий блок, записанный на другой даек. Например, если вы имеете четыре давка в матрице RAID 6, каждый участок будет записываться на один из трех блоков данных и один блок дублирующей информации. Дублирующая информация будет перемещаться от даска к диску, так что вдкаков даек не содержит всю дублирующую информацию. Когда отдельны! диск отказывает в матрице RAID 5, другие давки примут нагрузку на себя. Когда нужно читать с отказавшего диска, контроллер или ОС будут читать дублирующую информацию и сравнивать ее с данными от всех других неповрежденных дисков, вычисляя данные, которые в противном случае находились бы на отказавшем диске. Конфигур ация RAID S очеаь похож» ни RAID б за коключекием того, что вместо распределения дублирования на все диски в матрице для хранения резервных данных используется единственный даек, а все другие диски используются для хранения данных.
Совершение очевидно, что отказ даска в конфигурации RAID 5 означает, что чтение данных становится существенно медленнее, чем в случае отказа системы RAID 1, где чтение данных фактически не затрагивается. Также очевидаю, что издержки, связанные с поддержкой избыточности — только 1/п от нашей области для размещения данных, где п — число дисков, входящих в матрицу. Другое преимущество матрицы RAID 4 — то, что массив может быть очень большой размерности. Я видая массив ВАШ о с сотнями гигабайт. Для огромного большинства серверов и приложений среднего размера аппаратно-организованные структуры ВАШ 5 являются предпочтительными дисковыми подсистемами. ПАЮ 1О где О + 1 не равняется 7 Как вы можете видеть, RAID О имеет лучшую производительность и расширяемость всех массивов, но ие имеет никакой избыточности. RAID 1 имеет лучшую избыточность, но очень ограничена ее расширяемость. Таким образом, лучшие матрицы ВАШ сегодня на рынке те, которые определяются как RAID 0 + 1 или RAID 10. Чтобы построить матрицу RATO 10, вы сначала делаете две отдельных, но идентичных матрицы RAID О, где данные разделяются на блоки на двух или больше дисках. Эти две идентичные матрицы RAID 0 затем зеркально отображают друг друга. Данное решение реализуется только на высококачественных контроллерах RAID. В то время как RAID 10 часто рассматривается как одно из лучших решений RAID, следует отметить, что RAID 5 и КАШ 1 часто используются из-за их более низкой стоимости и равной избыточности. Решения для нескольких контроллеров/каналов До сих пор мы адресовали только один канал данных. Многие контроллеры могут иметь двойные каналы, и, конечио, вы можете устанавливать на сервере дополнительные контроллеры, Вспомните, что SQL Server — существенно много уровневая система. Когда отдельная запись пишется на диск, этому предшествует запись загрузки транзакции и очень часто запись одного или большего количества индексов. Когда SQL записывает информацию, параллельно этому ОС Windows NT или Windows 2000 непрерывно сокращает память ОС, постранично помещая на диск неактивную память, Вели зги записи могут быть записаны различными контроллерами на различные подсистемы матриц дисков, то проияводительвость транзакций будет умиожена.
Чтение данных с диска в каш данных также может извлечь существенные выгоды из нескольких каналов. Хорошо оптимизированная ВД Судет иметь дополнительные таблицы, которые чаще всего соединены. Помещая данные из одной таблицы е помощью контроллера А, индексы этой таблицы — контроллера В, данные из другой таблицы — контроллера С, индексы этой таблицы — контроллера D и TempDB (временная таблица БД) — с помощью контроллера В, чтение данных может выполняться одновременно. И бистро. В целом, в то время как структура RAID 0+1 выла бы, вероятно, самой быстрой единственной дисковой подсистемой для одного и того же бюджета, лучшим решением с точки зрения стоимости для большинства приложений будет покупка трех или четырех подсистем RAID 5, Тогда загрузкам транзакций будет отведена одна подсистема, данным от некоторых таблиц и индексам от других — другая подсистема, и остающимся таблицам и индексам ст предыдущих таблиц — третья подсистема. Файл страниц и другие компоненты ОС могут быть ва четвертой подсистеме, если она доступна. Другое жизнеспособное решение, которое часто используется, состоит в том, чтобы поместить интенсивно записывающую загрузку транзакции иа зеркально отображающую конфигурацию RAID 1. Записи RAID 1 быстрее, так как зеркально отображающие драйверы выполняют одновременную запись и не имеют издержек на вычисление и запись дублирующих частей. Так как данные требуют последующего расширения, одна или большее количество подсистем RAID 5 включаются для данных и индексов. Настройка диска и контроль работы В одном из предыдущих разделов Искусство управления работой упоминалось о введении счетчиков работы диска, Счеттаки издержек работы диска, связанных с использованием ЦП, очень малы. На заре Windows NT было определено, что счетчики издержек работы диска были в худшем случае равны половине процента от использования процессора на одном сервере CPU 486DX66 Server, С Pentium 3, работающим с десятикратным увеличением тактовой частоты в симметричных мультипроцессорных структурах, значения счетчиков работы диска пренебрежимо малы. Самый серьезный риск узкого места после памяти одинаково распределен между дисковой подсистемой и процессором(ами). Просмотр большой таблицы может буквально поставить сервер на колени. Еще хуже опрометчивое декартово соединение двух больших таблиц — смертельный приговор для работы диска. Вы будете хотеть управлять работой диска, когда такие события просачиваются в ваши системы (и они будут в самые неподходящие времена). Вообразите руководителя людскими ресурсами, пытающегося ал пустить итоговый запрос во время процесса формирования платежной ведомости. Это случается. Начните управление работой даска, просматривая следующие счетчики: □ PhysicalDisk: % Disc Time (физический диск: процент дискового времени^ □ PhysicalDisk: Current Disc Queue Length (физический диск: текущая длина очереди к диску); □ PhysicalDisk: Avg. Disc Queue Ler.cth (физический диск: средняя длина очереда к диску). Приложения и системы, связанные с ввод ом/вывод ом, могут постоянно держать даек активным. Это называется перегрузкой диска.
Вы должны всегда знать, сколько каналов, какие типы матриц, сколько дисков находятся в каждой матрице н в каких матрицах/каналах расположены ваши данные и загрузки транзакций перед тем, как вы решили заняться настройкой работы джека. Счетчик PhysicalDisk: % Bisk Time контролирует процент времени, которое диск тратит на проверку счетчика PhysicalDisk.: Current Disk Queue Length, чтобы посмотреть число запросов в очереди, которые ожидают доступ к диску. Важно при этом быть знакомым с вашей дисковой подсистемой. Если число запросов, ожидающих ввода/вывода, непрерывно составляет величину, в 1.М раза больше числа дисководов, составляющих физический диск, вы имеете иа диске узкое место. Например, конфигурвция RAID 5 с семью дисководами/дисками была бы кандидатом на настройку работы диска, если Current Disk- Queue Length будет постоянно держаться на значениях более чем 12+14. Чтобы улучшить работу ж агой ситуации, рассмотрите добавление более быстрых приводов диска, перемещение некоторых процессов на дополнительную подсистему контроллера-диска или добавление дополнительных дисков к матрице RAID 5 Большинство дисков имеют один дисковод, хотя устройства RAID обычно имеют больше. Аппаратные средства устройства RAID б a Windows NT PerfMon или Windows 2000 SysMon рассматриваются как один физический диск. Устройства RAID, созданные с помощью программного обеспечения, ведут себя как несколько дисков. ПРЕДУПРЕЖДЕНИЕ: Счетчик % Disk Time может указывать значение, большее чем 100 процентов, если вы используете аппаратно-организованную конфигурацию RAID. Вели это так, используйте счетчик PhysicalDisk: Avg. Disk Queue Length, чтобы определить среднее число запросов системы, ожидающих доступ к диску. Он опять-таки указывает на проблемы в работе, если постоянно его значение в 1.5+2 раза больше числа дисководов в матрице. Соединения пользователя В SQL Server 6.5 соединения пользователя были важным местом настройки. SQL Server 7 и SQL Server 2000 уменьшили эту важность. Соединения пользователя в этих версиях являются динамические, самоконфигурируемым выбором, В большинстве случаев вам же нужно регулировать назначения соединений пользователя, потому что если необходимо 100 связей, то и буяев размещено 100 синаев. Запуск следующих команд даст текущие назначения соединений пользователя и максимум допустимого числа соедивении пользователя; IEXEC spconfigure "user connections" SELECT ввМАХ_С0КМКТ10КЗ Вы можете использовать sp_ccn£igure, чтобы настроить число соединений пользователя, но мы иастоятельно рекомендуем взбегать это a SQL Server 7 и SQL Server 2000. Каждое соединение пользователя потребляет приблизительно 40 Кб памяти. Когда вы начинаете определять максимальное число соединений пользователя вместо разрешения SQL Server обслуживать нужное их число, распределение памяти больше ие будет динамическим.
Вместо этого будет сразу же использовано 40 Кв памяти, умноженное на максимальное число соединений. Вообще, вы можете захотеть конфигурировать число соединений пользователя в оборудовании только тогда, когда незаконные пользователи могут породить большое количество соединений. Многие администраторы и менеджеры путают соединения пользователя (User Connections) н лицензии доступа клиента (Client Access Licenses — CAL), Dro все равно что яблоки и апельсины. В SQL Server 2000 CAL — лицензия для единственного устройства (обычно компьютера, но это может быть и ваша телефонная ячейка), чтобы соединиться с SQL Server. Люди, которые управляют вашими лицензиями, должны явно назначить ее устройству. Как только это устройство залицензировано единственной CAL, оно может соединяться и открывать столько соединений пользователя, сколько позволит сервер. Вели способ лицевзирования для вашей CAL — "На место” (РЕК SEAT), можно открыть аополнитольные соединения пользователя с другим SQL Server. Неудивительно видеть, когда тпельяын компьютер е единственной CAL "На место” открывает 20 соединений пользователя к одному серверу и пять — к другому. Короче говоря. нет никакой корреляции между соедииетаями пользователя и CAL. Пожалуйста, не думайте, что это короткое объяснение решает все вада юридические проблемы (которые существенны для CAL). Структура лицензирования Microsoft более сложна, чем это короткое объяснение различия между соединениями пользователя и CAL. Профессиональный вывод, который следует сделать, — ЧИТАЙТЕ ВАШИ ЛИЦЕНЗИИ. Важно контролировать соединения пользователя при вашей настройке работы. Иснодьземый счетчик — SQL Server: General: Uaer Connections (SQL Server: общие: соединения пользователя). При поиске неисправностей иди проверке оборудования вы иожете захотеть проконтролировать SQL Server: Databases: Active Transactions: database (SQL Server: базы данных: активные транзакции: база данных). Число соединений пользователя основано иа требованиях вашего приложения ж пользователей. Приложения OLE DB нуждаются в соединении для каждого открытого объекта связи. ODBC-соединения порождаются для обработки каждого активного соединения в приложении. Библиотека ВД использует соединение для каждого использования функции dbopen. n-ярусные приложения, которые используют объединение соединений, стремятся минимизировать число открытых соединений. Так как SQL Server является системой, загружающей транзакции, все, что вы можете пожелать ввить — это присутствие длинных работающих транзакций. Обычно нужно сбрасывать загрузки транзакций каждые чае или два. Загрузка транзакции остается открытой, пока открыта самая длинная транзакция. Если транзакция остается открытой на больший период времени, загрузка транзакции будет расти и расти, с каждым сбросом становясь все больше и больше. По умолчанию SQL Server будет автоматически увеличивать загрузку транзакции. Она может стать действительно огромной, и мак мы можем видеть, достигнуть емкости диска. Мы проследили приложения третьих лид, которые начинают сеанс с запроса функции dbopen библиотеки ВД, немедленно следуемой за оператором beg IN TRANSACTION. Контролируйте размеры дампов ваших загрузок транзакций. Если они получают постепенно больше информации в течение дая для конкретной ВД, вы можете определить это условие. Можно наблюдать длительиую работу открытых транзакций, выполняя комавду T-SQL: DBCC OFENTRAN <база_ данных>
Вы можете даже рассмотреть запись на языке Т-SQL, которая обеспечивает вам наличие длинных работающих открытых транзакций. Запирание и блокировка Запирания — сами по севе очень хорошая вещь. Они гарантируют, что данные, которые вы читаете, — фактические данные в ВД. Запирания, сохраняющиеся слишком долго, — проблема. Это приводит к тому, что мы называем блокировкой. Демонстрация блокировки Запустим Query Analyzer (анализатор запросов). Откроем два соединения к одному и тому же серверу. Затем состыкуем окна вертикально, используя опцию меню Windows | Tile Vertically. В первом окне напишем и выполним следующий код: И USE pubs GO SELECT aufname ГЖН authors — (NOLOCK) В WHERE au_lneme - 'Green' Вам следует вернуть значение Marjorie. Во втором окне напишем и выполним следующий код: (USE pubs - - - GO BEGIN TRAN UPDATE authors SET au_fna~e « 'Harry' WHERE au_L.na.me •> 'Green' — ROLLBACK TRANSACTION Убедитесь, что имеются две черты перед оператором ROLLBACK TRANSACTION. Вы должны получить сообщение. (1 row(s) affected! — воздействует иа 1 строку Теперь заново выполним первый запрос. Откройте третье окно (набравCtrl-N) и запустите команду: Ц sp_who2 Просмотрите список и обратите внимание яа столбец blkby. Он показывает, что ваша транзакция SELECT блокирована вашей транзакцией UPDATE. Закройте третье окно. Если необходимо, перестройте ваши пиня, снова используя опцию меню Windows | Tiie Vertically, В вашем окне транзакция UPDATE выделите фразу ROLLBACK ТЯДЫВЛСПОМ и выполните ее. Заметьте, что произойдет с запросом SELECT. Повторите эксперимент снова, только иа этот раз ие помещайте в комментарий оператор (NOLOCK), удаляя две черты перед ним. Так как вы теперь используете NOLCCK, вы не увидите ваш оператор SELECT, на сей раз блокированный в sp_who2. Это — хороший пример испорченного чтения.
Контроль блокировок Иногда что-то нельзя сделать лучше, чем есть. В SQL Server 6.5 имелся очень удобный счетчик SQLServer-Locks: Users Blocked (блокировки SQL Server: блокированные пользователи), который мы всегда использовали, чтобы показать активные блокировки наших серверов. Это был такой важный счетчик, что мы увеличили масштаб счетчика в 10 раз и увеличили ширииу счетчика иа очень толстую линию. Таким образом, когда даже единственная блокировка возникнет в наших серверах, в диаграммах контроля работы появится большая толстая линия. К сожалению, этот счетчик ушел из SQL Server 7 и не используется в SQL Server 2000, Что делать? Используйте User Definable Counters (счетчики, определяемые пользователем). Большинство людей никогда не использует эти счетчики и не знают, как они работают. В SQL Server в.5, когда вызывался счетчик, он автоматически обновлялся, В SQL Server 7 и SQL Server 2000 это ие так. Каждый определяемый пользователем счетчик должен быть явно обновлен значением целого числа, запустив хранимую процедуру sp_user_counterX Y, где X — номер определяемого пользователем счетчика я ¥ *— целое число, задаваемое счетчику. В SQL Enterprise Manager для SQL Server перемещайтесь по дереву управлений к Management | SQL Server Agent | Jobs (Управление | Агент SQL Server | Виды работ). Щелкните правой клавишей по Jobs и выберите New Job (новая работа). Затем введите название и описание для User Defined Performance Counter 1 (Счетчик 1 с характеристиками, определяемыми пользователем). New Jnh Properties - UM \MSBni Gensial | £i«ps | SsheAfes | NoHcarions | фр8™ Cceated Cstegcxy Cwner Last modied (Not acpicebs) | UK I Caned; | -. Арй j Нф 1" 1 — 1 1-----------“--------------1--------- Щелкните по закладке Steps (шаги) и затем по кнопке Nev,' (новая), чтобы добавить шаг. В диалоговом окне New Job Step (новый шаг работы) совдайте в этой работе единственный шаг с именем Reset Counter (исходное состояние счетчика) со следующим фрагментом ва T-SQL, выполняемым в ВД master:
ISET NOCOUNT ON DECIARE ^blocks int SELECT eblocks - COUNT(blocked) FROM mas tai.dbo. sysp races s e s(NOLOCK) WHERE blocked > 0 EXEC sp_usee_counterl ^blocks GO Окно будет выглядеть следующим образом: Щелкните по кноке ОК, чтобы закрыть диалоговое окно New Job Step. На закладке Schedules (расписания) щелкните по кнопке New Schedule (новое расписание). Установите выключатель Start whenever the CPU(s) become idle (запускать всякий раз, когда ЦП станет свободным), так как мы хотим, чтобы эта работа выполнялась в фоновом режиме и не сталкивалась с Ваяве важными. Heme: ’ (when Idle Р gnsbted :• SchesjfeTjipe.... ............ -...............- - . glad sutometce^iwtBnSjLSevsAseni slais I <• Stat whenever ihe£P1J[*) become Йе! <' gne’jme | 1 z ’/2ИЛ 3 ;<>« Г Якитга OK [ Cwaat | Help |
Щелкните по кнопке ОК, чтобы сохранить это расписание, и по кнопке ОК в окне New Job Properties, чтобы сохранить работу. Теперь или в WindowB NT PerfMon, или Windows 2000 SysMon добавьте экземпляр SQLServer: User Settable: Query: User counted (SQL Server: установка в начальное состояние: запрос: счетчик 1 пользователя). Запустите проверку блокировки, приведенную в разделе Демонстрация блокировки, чтобы проверить вашу работу. Если вы зятем решите добавить другие определяемые пользователем счетчики характеристик, вам нужно будет только добавить их во фрагмент на Т-SQL на шаге Reset Counter, который вы огфеделили ранее, и активизировать счетчик в Windows NT PerfMon или Windows 2000 SysMon. Учебный пример В этом разделе нашего учебного примера мы рассмотрим получение объемных значений, которые собрали в главе 8, н в конце превращение их в реальные числа. В следующей таблице мы взяли таблицу ие главы 8 и теперь проэкстрапол ируем, какие в ней появятся новые значения, поскольку у нас теперь есть несколько новых таблиц. Наиболее примечательны в них следующие три строки: Название таблицы Начальное число СТрОК Увеличение строк за месяц Максимальное число строк Deposit 0 5 4000 Check 0 245 10000 DirectWit-hdrawal 0 50 1000 В ваших таблицах мы взяли таблицы подтипов транзакции и перевели их в таблицу transaction. Добавим их все вместе в йдинегаенную отрежу: transaction 0 300 15000 Мы делаем то же самое для каждой из таблиц и получаем следующую таблицу начального количества строк, увеличения строк и максимального ожидаемого числа строк: Название таблицы Начальное число строк Увеличение строк за месяц Максимальное число строк account 1 0 20 accountReconeile 0 1 Зв directwithdrawal 0 50 1000 address 50 30 600 addressType 5 0 10
Название таблицы Начальное число строк Увеличение строк за месяц Максим алъное число строк bank 1 0 5 city 450 20 25000 payee 100 30 300 payeeAddress 100 30 «00 payeePhoneNumber 100 30 600 phoneNumber 50 30 600 рЬопеМшпЬегТуре 5 0 10 state 50 0 70 statement 0 1 Зв statementltem 0 зоо 15000 statementType 3 0 5 transectionAllocation 75 «0 12000 transaction 0 300 15000 transactionAllocationType 20 0 40 transactionType 3 0 5 user 10 2 50 zipCode 1000 10 99999 zipCodeCityReference 1100 10 09999 Затем мы должны вычислить размеры таблиц. Используя процедуру sp_table9indexSpace, вычислим значении для числа начальных строк, роста строк в месяц и максимального числа строк. Для каждой таблицы должно быть сделано предположение относительно процента, до которого заполнены столбцы переменной длины. Для этого учебного примера мы примем 50 процентов. Типвной запрос для максимального числа строк в таблице transaction показан ниже. Имеются три столбца переменной длины в таблице t г an section! number (номер) типа varchar (20), signature (подпись) типа varchar (20) и description (оппеянис) типа varchar(1000), Нужно оценить значение предполагаемых 50 процентов для них > памяти. IEXEC ep_table$indexspace etableSame « 'transaction', SvarFillPercent = 50, enutn_rows = 16000 Результаты для всех таблиц и всех условий, которые мы рассмотрели, представлены в таблице ниже. Когда вы посмотрите иа результаты для каждой таблицы, можно заметить, что увеличение в месяц не кажется нарастающим. Это потому, что мы использовали строки
в месяц как входной параметр строк для хранимой процедуры sp_table$indexSpace. Будет ли одна строка или несколько, выходам величина процедуры будет всегда кратна 8 Кб. Таким образом, размер особенно уместен для начального количества строк и максимального числа строк. Также помните, что при росте индекса уровни узлов постепенно заполняются, В некоторый момент они потребуют "вырастить" дополнительный уровень узлов-листьев. Таким образом, рост индекса вообще-то будет нелинеен, особенно для больших таблиц. Размер кластерного индекса относительно гладкий (единственная страница корневого узла в 8 Кб) фактически для всех условий. Это потому, что кластерный индекс, о котором мы говорим здесь, является единственной страницей корневого узла, которая указывает на страницу фактических даиных. Все наши кластерные индексы — ключи иа одного столбца типа 4-байтовых целых чисел, и 465 указателей страниц будет находиться на странице корневого узла. Это будет почти всегда давать самый гладкий реальный кластерный индекс, который вы можете получить. Как только страницы данных превысят 465 страниц, у кластерного индекса увеличится уровень и индекс включит дополнительный уровень узлов индекса, Это видно только в таблице transaction для максимального числа строк. Из-за относительно большого размера строк в таблице transaction (помните, что столбец description — varchar (1000), и мы предполагаем, что 50 процентов заполнено), число страниц превышает порог в 465 страниц. Так как все наши таблицы имеют кластерные индексы, более точно было бы сказать, что размер кластерного индекса равен сумме столбца "Размер кластерного индекса" и столбца "Размер данных в Кб", который является фактически узлом-листом кластерного индекса. Мы получаем их в хранимой процедуре и помещаем ниже, чтобы их можно было проанализировать. t 1 S и а Таблица Условие Строки Размер данных в Кб Размер кластер® Уровни кластер! Размер иекласте индекса Уровни .некалей индекса account Начальное число строк 1 8 8 1 8 1 account Увеличение за месяц 0 0 0 0 0 0 account Макс, число строк 20 8 8 1 8 1 accountReconcile Начальное число строк 0 0 0 0 0 0 accountReconcile Увеличение за месяц 1 8 S 1 8 1 accountReconcile Макс, число строк ЗВ 18 8 1 24 2 address Начальное число строк 50 24 8 1 32 2 address Увеличение за месяц 30 1в 8 1 24 2
е g g 2 § 8 о О й, О. Таблица Условие Строки й и а|5в|ЯаЙ8 s 8 * В 5 у S К в S Б в. в Б ® 8 й v s я ® Я вм я я S 2 я м я 0 5 в 5 * в я I ft и я в ® я £| з bssbas>, Sabss в s address Макс, число 600 272 8 1 296 3 строк addressType Начальное 5 8 8 1 8 1 число строк addressType Увеличение 0 0 0 0 0 0 за месяц addressType Макс, число 10 8 8 1 8 1 строк bank Начальное 1 8 8 1 8 1 число строк bank Увеличение 0 0 0 0 0 0 за месяц bank Макс, число 5 8 8 1 8 1 city Начальное 450 32 8 1 40 2 число строк city Увеличение 20 8 8 1 8 1 за месяц city Макс, число 25000 1456 8 1 1496 3 строк payee Начальное 100 8 8 1 8 1 число строк payee Увеличение 30 8 8 1 8 1 за месяц payee Макс, число 300 24 8 1 32 2 строк payeeAddress Начальное 100 8 8 1 8 1 число строк payeeAddress Увеличение 30 8 8 1 8 1 payeeAddress Макс, число 600 24 8 1 32 2 payeePhoneNutnber Начальное 100 8 8 1 8 1 число строк payeePhoneNumber Увеличение 30 8818 1 за месяц Продолжение таблицы на следующих страницах
ТаСляца Условие Строки а й « 3 X а в 2 я а ft я я 2 в я а § Я | и & £ в я Уровни кластерного S о % © § я Размеру шмслайерного индекса 8 а и а i |1 а 1 i** © я I ПШ0Ш1И payeePhoneNumber Макс, число 600 24 8 1 33 2 строк phoneNwnber число строк 50 8 8 1 8 1 phoneHumber Увеличение 30 8 8 1 8 1 за месяц phoneNumber Маис, число 600 32 8 1 40 2 phoneNumberlype число строк 5 8 8 1 8 1 phoneNumberType Увеличевие 0 0 0 0 0 0 за месяц phoneNumberType Макс, чпсля строк 10 8 8 1 8 1 state Начальное 50 8 8 1 8 1 state Увеличение 0 0 0 0 0 0 state Макс, число 70 8 8 1 8 1 statement К 8 8. Ч к л и : 5 * > Е 5 1 щ В* 0 0 0 0 0 0 statement Увеличение 1 8 8 1 8 1 за месяц statement Макс, число Зв 8 8 1 8 1 statementltem Начальное 0 0 0 0 0 0 число строк s ta temen 11 tern Увеличение 300 32 8 1 24 2 за месяц statementitem Макс, число 15000 1584 8 1 600 2 statementType Начальное 75 8 8 1 8 1 число строк statementType Увеличевие 400 48 8 1 32 2 за месяц statementType Макс, число 12000 1264 8 1 672 2 строк
2 2 Я & с 2 3 € а. fil Таблица Условие Строки a ® 2 Я S н - я * я СЬ 11« 4 I > Ш | i ? £ s h в S « ч 8 * 1 S 8SS Si 11 5 Й * И ч 0 S 8 0 0 fc H й< Я Я h й я i. я s й Я Я transaction Начальное 0 0 0 0 0 0 transaction Увеличение 295 178 8 1 24 2 за месяц transaction Макс, число 15000 9144 32 2 640 2 строк transactionMlccation Начальное 1 8 8 1 8 1 число строк transactianMlocation Увеличение 0 0 0 0 0 0 за месяц transacticnAHocation Макс, число 20 8 8 1 8 1 ft о rt о *0 Ф Начальное 3 8 8 1 8 1 число строк t ran sect i onType Увеличение 0 0 0 0 0 0 transactionType Макс, число 5 8 8 1 8 1 user Начальное 10 8 8 1 8 1 число строк user Увеличение 2 8 8 1 8 1 user Макс, число 50 16 8 1 8 1 zipCode Начальное 1000 82 8 1 40 2 число строк zipCode Увеличение 10 8 8 1 8 1 за месяц zipCode Макс, число 99999 2544 8 1 2658 2 zipCodeCityReference Начальное 1000 82 8 1 40 2 число строк zipCodeCityReference Увеличение 10 8 8 1 8 1 за месяц zipCodeCityReference Макс, число 99889 2936 8 1 2944 2
Полное пространство для таблицы таким образом будет равно размеру данных плюс размер кластерных и некластерных и и дексон. Наши таблицы имеют только по одному некластерному индексу для вторичного ключа. Очевидно, что когда вы добавляете больше некластерных индексов для улучшения функционирования, то нужно дополнительно рассмотреть результаты процедуры sp_table5indexSpace для каждого дополнительного некластерного индекса, которые нужно добавить в общие количества строк, перечисленные выше. Резюме В этой главе мы рассмотрели, как можно оценить требования, предъявляемые к вашей ВД не только во время запуска, но также и через год или больший промежуток времени. Оценка размеров данных и индексов и оценка строк тесно связаны с будущей работой. Архивирование — один из наиболее эффективных методов для получения существенного улучшения функционирования зрелой ВД. Мы рассмотрели наиболее важные требования к аппаратным средствам с точки зрения места храаення данных, производительности обработки и оперативной памяти. С этим связано требование, чтобы вы потратили время на ознакомление непосредственно со средствами анализа функционирования, чтобы, когда возникнет необходимость, вы имели бы навыки устранения причин, порождающих проблемы функционирования. Волве конкретно мы ууиде-яи, какие возможны конфигурации накопителей иа жестких дисках и как они влияют на работу и избыточность данных. Очевидно, чем более высокая скорость процессора, чем больше количество приобретенной памяти, тем лучше. Но мы также увидели, насколько важно правильно сконфигурировать SQL Server, чтобы воспользоваться преимуществам процессора и памяти, особенно когда сервер может бороться за ресурсы с другими приложениями, Мы теперь на стадии завершения проектирования нашей системы, которую продолжим в следующей главе.
Завершение проектирования Введение На протяжении всех тринадцати глав мы рассматривали нашу СУБД от концепции до рабочей БД. Мы собрали данные относительно того, что хочет пользователь, построили модели этих данных, создали таблицы, рассмотрели различные механизмы зашиты и, наконец, добавили код, который действует как своего рода связующее звено между пользователем и данными. Так что, наша работа подошла к концу? Ну, хотя OLTP-часть системы теперь и завершена, полна, мы все еще имеем несколько моментов, которые нужно привести в порядок. □ Настройка функционирования — Хотя мы коснулись выполнения настройки в предыдущих главах, но главным образом обращали внимание на то, как транзакции могут воздействовать на систему. □ Отчеты = Тетерь, когда наша OLTP-БД укрепилась и мы состоянии работать с .чей, можно оптимизировать ее с точки зрения требований отчетов, которые определены ка стадии логического прояетярования. Для больших специализированных проектов (особенно включающих большие количества данных, или даже механических клиентов, использующих данные двадцать четыре часа в сутки) нам может потребоваться сделать копии (или частичные копии) данных только для чтения, чтобы использовать их для отчетов. Это уменьшит конфликтные ситуации с данными в наших первичным ВД, Мы рассмотрим проектирование БД только для чтения, чтобы выполнить такие требования. Отметим также несколько видов использования выделенных структур только для чтения, отличных от простого формирования отчетов, типа формирования оперативного хранилища данных (Operational Data Store — ODS).
□ Моделирование предприятия — Как у архитекторов общпу. данных, одной из наших задач должно выть построение модели, которая ив только охватывает работу, которую мы сделали для одного проекта, яо и всех проектов, которые выли до атего, и даже новейшие пакеты, которые были созданы различными поставщиками. Цель моделирования предприятия состоит в том, чтобы иметь вались всех данных в нашей организации и увеличить полную нормализацию на общем уровне, □ Реализация с тремя стадиями — Овначает цикл проектирования и производства, включающие стадии разработки, проверки харктеристик и реализации. Мы обеудам, как строить такую конфигурацию. Вудут представлены несколько руководящих принципов, как обезопасить себя от системных проблем на сталии функционирования, типа перебоев в электропитании ж общих отказов аппаратных средств. Хотя настройка системы иа стадии s функционирования, строго говоря, не является обязанностью архитектора данных, ‘ на моем опыте архитектор данных часто еынужоен включаться и может быть ответственным, если соответствующее дублирование и плав при аварийных с- = ситуациях ве будут определены а структуре системы. Настройка функционирования Процесс настройки функционирования сложен и полностью отделен от проектирования ВД. Мы должны также иметь в виду, что это — бескоиечиыи процесс. Разработчикам может потребоваться повышение эффективности функционирования, и испытатели (особенно выполняющие напряженные испытания) с надеждой будут использовать почти каждую дополнительную возможность настройки функционирования, Как только реальная система создана и запущена, мы снова будем настраивать функционирование. Настройка функционирования — ответственность администратора ВД, в Ш архитектора данных. Тем не менее, при проектировании БД мы должны знать критические моменты, которые затронут функции администратора ВД, и должны регулярно с ним консультироваться. Следующий перечень содержит аришны, почему мы обходили стороной серывзное обсуждение настройки функционирования во время нашего проектирования: Настройка функционирования ве должна основываться на догадках — Функционирование должно рассматриваться дяя всей системы, а формирование ваших соображений насчет функционирования иа основе предыдущих экспериментов не всегда оптимально. SQL Server обеспечивает некоторые чрезвычайно мощные средства для выявления мест, где существуют проблемы в наших системах. Используя SQL Server Profiler, мы можем определить, какие запросы требуют длительное время на выполнение, и посмотреть, как долго будут протекать процессы до и после ивмеиения. Это, вместе с хорошими напряженными испытаниями, поможет определить, вляются ли индексы, усовершенствования аппаратных средств или даже денормализация лучшим вариантом для полного пакета системы. Следует мимоходом отметить, что проверка функционирования не означает изменения модели БД отменой нормализации и, разумеется, денормализации ради этого, так как она может просто привести к деградации функционирования при попытке управлять таблицами ВД. Хотя даяормализация может быть желательна в некоторых случаях, вы начнете заново вводить различные вилы аномалий корректировки, которые мы старались уда пить »тфоцессе нормализации. Нужно или жить с этими аномалиями, или и я писать дополнительный код, чтобы бороться е ними. Умелый администратор БД, который может настраивать БД без необходимости выполнять двиормализацию, стоит иа вес залога.
Настройка функционирования изменяется в различных версиях SQL Server — В то время как более ранние версии требовали, чтобы проектировщик выполнял большую часть оптимизации вручную, последние версии (7Л и 2000} добавили богатый спектр фуикциожальиых возможностей настройки функционирования. Вообще, я теперь пришел к выводу, что часто лучше всего отдать управление аптимизапией системе SQL Server. Дальнейшие детали по этому вопросу могут выть найдены в "РrofeMtaaal SQL Server 2000 Programming" (Wrox Press, ISBN 1861004486). Лишние индексы очень дорого стоят — В интенсивно записывающей системе типа OLTP-БД слишком много индексов могут вызвать проблемы функционирования. Это возникает вследствие того, что когда в осиовной структуре сделяиы изменения, мы существенно увеличивая* стоимость выполнения добавления, обновления или удаления в связи е тем, что приходится манипулировать и структурами таблиц, и индексами. При создании такой БД использование индексов может быть привлекательным решением, но это может воздействовать на полное функционирование системы, особенно, если данные непрерывно добавляются. С другой стороны, в случае БД или даже таблицы, которая часто не изменяется, соответствующее использование ин.тояло» может привести к; pgg-дьяаму улучшению функционирования - Когда возникают проблемы функционирования, мы можем использовать SQL Server РгоШег, чтобы пронаблюдать точные вызовы, которые делают наши клиенты. Query’ Analyser тогда позволит ия.м рассмотреть следующее: □ План запроса — Просматривая, как SQL Server планирует выполнять запрос, мы можем видеть, делает ли он какие-либо нелогичные предположения, которые мы должны улучшить, ада переписывая наши мпрае&- (подобно удалению из запроса данных, которые мы никогда ие используем), используя подсказки оптимизатора относительно улучшения запроса, или, возможно, добавляя индексы, но с соответствующим предостврвжеяиеи, как ^пышвуто выше. □ Временная статистика и статистика ввода./вывода — Мы можем установить Query Analyse, чтобы он возвращал информацию атновитвяьио примни, занятого запросом, или сколько и какого типа работы диска требуется для запри*. □ ''Мастер” настройки индекса — Использование этого средства определит, используете ли вы индексы, являющиеся оптимальными в вашей ВД. □ Команды DBCC — DBCC (Database Consistency Checker •— модуль контроля непротиворечивости БД) устввавливЛтся для контроля непротиворечивости БД, и имеется весьма немного очень полезных команд DBCC, которые можно использовать для настройки функционирования. Они включают SH"«_STATISTICS, которая показывает страницы статистики для индексов, чтобы помочь определить, почему они используются/ие используются, и SHOWCONTIG,которая иокяямямт, как фрагментирована таблица или индекс. Эти средства, однако, только скользят по поверхности. Имеется миого других методов, которые мы можем использовать для настройки запросов, БД п серверов, чтобы получить максимальную эффективность. Создание индексов всегда имеет смысл делать для улучшения функционирования, но они могут дать слишком много реальных проблем. Можно предпринять ограниченную денормализацию типа отмены разбиения на уровне конкретных таблиц, где имеются проблемы (обычно связанные с отменой разбиения 4НФ или выше), so только после того, как Выл выпалввж некоторый уровень испытаний.
Ди удачного начала настройки БД в SQL Server можно рассмотреть следующие книги: □ "Inside SQL Server 2000' (Microsoft Press. ISBN ШММ985) □ "Professional SQL Server 2000 Programming" (Wrox Press. ISBN 1861004486) □ "Professional ASP Data Access" (Wrox Press. ISBN 1861003927) Поддержка БД только для чтения Если наши потребности формирования отчетов не в режиме реального времени, то есть мы можем позволить некоторое время ожидания между текущими данными и данными, с которыми пользователи имеют дело, то мы можем построить БД только для чтения, которая является зеркальным отображением наших активных БД. Пользователи, которым нужно запускать дорогостоящие запросы отиосительво данных, могли бы тогда использовать эту БД только для ч«етя, В зависимости от того, как БД используется (и после того, как мы загрузили данные в БД), можно установить ВД только для чтения, используя следующий оператор: ВЖС sp_dboption '<имя_ВД>', 'read only’, 'true' Мы можем затем переключиться назад в режим записи всегда, когда нам нужно загрузить дополнительные данные в БД (так как нельзя использовать режим только для чтения, когда вы желаете освежить БД). Задание БД режима только для чтения имеет очень большие преимущества, сообщая SQL Server, что он фактически не должен использовать блокировку ресурсов БД, так как никто не может делать изменения данных или структур БД. Обычно мы стараемся выполнить все записи в базах, используемых только для чтения, когда никакие пользователи ие хотят читать данные. Прежде чем нойте двдешв, нужно ответить на вопрос: "Почему нужно создавать БД только для чтения?” Ответом на этот вопрос может быть следующее: 1. Маясимизироввть параллелиэм в наших пользовательских системах БД. В. Максимизировать функционирование на наборе запросов. 3, Объединить данные иа нескольких источников. Суть заключается я том, что хотя ясе наши данные должны шеп свой оригинал в соответствующей нормализованной OLTP-БД, имеются ситуации, где это определенно не будет иметь место: □ Наследуемые БД — универсальные ЭВМ обычно упоминаются как наследуемые системы, но поскольку прошло только несколько лет, как мини- и даже PC-платформы начали развивать платформы сервера, мы можем получить наследуемые БД, основаивые на любой платформе.
□ Пользовательские ВД — к сожалению, не все ВД построены на сервере БД. Существует много таких, как Access, Foxpro или другие платформы ВД. □ Другие — категория "другие" названа так потому, что имеется слишком много вариантов, чтобы их упоминать. Электронные таблицы, текстовые файлы и много других способов хранения данных могут быть использованы в конкретной организации и могут потребоваться для вводя в системы, используемые только для чтения. Как только мы преобразовали данные из наших ВД в систему, используемую только для чтения, мы получим следующие преимущества: □ Ограниченное число конфликтных ситуаций — так как все пользователи ВД будут неспособны изменить данные, имеется очень небольшой шанс, что параллельные пользователи будут блокировать одни и те же части данных ®уг от друга. Всякий раз, когда мы можем гарантировать, что никакие процессы не будут записывать данные в ВД, мы можем задать для БД режим только для чтения, и никакие блокировки не будут работать. Если мы имеем непрерывно обновляемую систему, то могли бы запустить отчеты на повторяемом читаемом уровне изоляции, который будет означать, что мы не должны волноваться относительно внешнего процесса, изменяющего результаты наших отчетов, когда мы их запускаем. Заметьте, конечно, что мы яе можем исключить конфликтную ситуацию в пределах нашей подсистемы аппаратных средств или методов загрузки, так как когда порция данных копируется из нашей OLTP-системы, она будет блокирована для пользователей, явка идет передача данных. □ Быстрое функционирование — в зависимости от ситуации кснкретные запросы в нашей пользовательском ВД могут быть оптимизированы, ила, если желательно, мы можем удалить все запросы из пользовательской БД, Имейте в виду, что большие преимущества функционирования будут достигнуты, когда ВД, используемые только для чтения, не находятся на том же сервере, что и пользовательские ВД. В следующем разделе мы рассмотрим несколько возможностей для структурирования вашей ВД только для чтения, обсудим влияние на функционирование, дадим несколько советов реализации решения и, наконец, несколько примеров того, как использовать такие ВД на практике. Следует отметить, что в наших обсуждениях мы не переходам на уровень хранилища данных или витрины данных (реализованных в OLAP). Вместо этого мы рассматриваем более общие решения иепосредответых проблем, как мы обсуждали в главе 9, типа интенсивного использования кяйеятов, шрееламшапировтноеа формирования отчетов для текущих данных и ODS. Активность чтения — очень важная часть разрешения проблемы для нашей OLTP-системы, чтобы остаться настолько нормализованной, насколько возможно, забрав нагрузку от наших серверов и особенно сокращая состязание между читающими и пишущими пользователями.
Моделирование изменений В этом разделе мы будем использовать следующий набор из четырех простых таблиц для иллюстративных целей: Здесь product — таблица "изделие'; produatld — идентификатор изделия; number — номер (изделия); лвие — название (изделия); price — цена; cast стел — тавлица ''заказчик'’; custonterM — идентификатор заказчика; names — имя (заказчика); invoice — таблица "счет-фактура"; invoiceld — идентификатор счета-фактуры; date — дата: number — номер (счета-фактуры); invoiceXineltem — таблица "элемент счета-фактуры"; InvolceLineltemld — идентификатор элемента счета-фактуры; quantity Purchased -— проданное количество. Очевидно, что это является упрощенной моделью системы счета-фактуры, и что гая, например, игнорировали факт, что значение цены изменится через какое-то время. Однако этот набор таблиц будет служить иллюстрацией общей системы, «Куда должны быть получен отчеты. В действительности, мы можем raters намного более сданную ситуацию, наподобие следующей: Здесь ИУС — информационно-управлятцал система. воо
Это означает, что ваше хранилище данных может получать данные более чем от одной БД. Обратите внимании, что я включил в диаграмму электронные таблицы Excel — это не ошибка! Это не исключительный случай, когда электронные таблицы являются "домом” части данных, особенно, когда нет технического анализа при создании и управлении ВД. Заметьте, что мы не защищаем, конечно, использование электронных таблиц как хранилища данных, просто допускаем, что это часто случается. К счастью, мы можем импортировать данные почти отовсюду, используя сервисы преобразования данных, создавая ADO-клиенты или используя некоторые из функций в SQL Server, которые мы обсудили. Моделирование систем, используемых только для чтения, похоже да задачу моделирования OLTP-БД, за исключением того, что мы можем в значительной степени игнорировать правила нормализации. Имеются два метода, которые я нашел полезным и для выполнения этой задачи. Один яваляется очень прагматичным, а другой — открытым решением, которое позволяет в более свободной форме просматривать данные: □ Ориентируемый на таблицы — Общедоступный метод созданий ВД для отчетов, где представлена каждая важная таблица, которая не содержит лишь данные домена. □ Ориентируемый на решение — Если мы должны решить очень конкретную проблему, то обычно не нужно использовать столь радикальный подход, как предыдущий метод. Мы обсудим, почему мы могли бы пожелать включить только то, что необходимо для обеспечения конкретных нужд. Имейте в виду, что как всегда я не утверждаю, что они являются единетаенньши методами для выполнении этого вида действия. Перечисленные здесь методы являются пищей для размышления, так как они являются точными решениями конкретных проблем. Так как мы рассматриваем ВД только для чтения, единственное требование — то, что данные в ВД соответствуют исходами данным для некоторого конкретного момента времени. Кроме того, структура вызывает меньше беспокойства, потому что данные не изменяются (за исключением системных процессов, изолированных от пользователя!. Метод, ориентируемый на таблицы Что мы должны сделать, так это ваять копию нашей OLTP-ВД и расширить каждую сущность так, чтобы она включала столько данных из таблиц-предков, сколько возможно. В нашем сценарии примера таблица invoice (счет-фактура) имеет в качестве предка таблицу customer (заказчик), а таблица involaeLlneltem (элемент счета-фактуры) имеет invoice и customer с одной стороны и product (изделие) — с другой. В этом случае сущность и определевие таблицы : r.voiceLineltem включает определение того, что было куплено, счет-фактуру, заказчика и изделие. В таком подходе есть преимущества, так как когда пользователь хочет посмотреть invoiceLineltem, он обычно будет хотеть знать, какой заказчик купил конкретное изделие. Следовательно, мы включаем ссылку на заказчика в таблицуinvoicalineltern. Одна концепция в этом сценарии ие изменяется — каждый атрибут объекта должен описывать объект хотя бы только косвенно. Так как мы будем включать информацию о заказчике как элемент строки счета-фактуры, мы явно нарушаем правила нормализации, хотя значения в таблице фактически описывают информацию строки счета-фактуры, а именно, заказчика, который купил изделие.
Мы можем также включить дополнительные столбцы для итоговых данных, которые характеризуют объект. Если мы хотим иметь общую сумму всех счетов-фактур» те поместим это в таблицу invoice, а ие в invoicelineltem, Цель ие в том, чтобы полностью устранить соединения, а чтобы иметь возможность организовать разумную логическую структуру для оптимизации всего нашего функционирования. Следует упомянуть off одной дополнительной проблеме. Дисковое пространства. Наши, потребности в дисковом пространстве, расширенные So окончательною формата, будут большими и в некоторых случаях станут фактическим препятствием в зависимости от размера исходных Ванных. Очевидно, вся система не должна формироваться таким образом, если требуются только небольшие ее части Оля обеспечения запросов, Первым шагом должно быть перемещение по нашим деревьям отношений и получение атрибутов. Из диаграммы нашего примера мы спускаемся сверху вниз, начиная с invoice и добавляя поля предка следующим образом: ------------------ I invoice customer j customer id р I мте j i invoice-id custom er id 'FK) date number ______ customecngme 39ecъ customer — таблица ’заказчик"; customerld — идентификатор заказчика; name — имя (заказчика); invoice — таблица "счет-фактура'’; invoiceld — идентификатор счета-фактуры; date — Вата; number — номер: ctistoaer_namo — имя заказчика. customer_name теперь является частью таблицы invoice, так как счет-фактура предназначен для заказчика. Столбец customerld уже присутствует в табаице customer, так что не нужно ничего предпринимать. Следующий шаг — посмотреть на таблицу invoiceLineltem. Мы добавляем сюда атрибуты таблиц invoice и produce и получаем следующее: customer custo.Tierid Jneme Здесь product — таблица “изделие”; product Id — идентификатор изделия; number — номер (изделия); пате — имя (изделия); price — цена (изделия); customer — таблица "заказчик”; customerld — идентификатор заказчика; пате — имя (заказчика);
invoice — таблица "счет-фактура'; invoiceid — идентификатор счета-фактуры; date — дата; number — номер (счета-фактуры,); InvaiceLineltem — таблица "элемент счета-фактуры"; InvoiceLineltemTd — идентификатор элемента счета-фактуры; quantityPurchased— купленное количество; invaice_custamerld — идентификатор счета-фактуры-заказчика; invoice_date — дата счета-фактуры; invoice_number — номер счета-фактуры; custome^naae — имя заказчика; prccftict_number — номер изделия; product_name — имя изделия; product-price — цена изделия. На следующем шаге следует взять любые величины, е которыми ням нужно выполнить вычисления или операции объединения строк. и добавить поле, содержащее результат, В таблице invciceLinelteai мы имеем столбец quantityPurchased, а также столбец product_crice, и мы хотели вы перемножить значения этих волей, чтобы найти значение общей суммы дня элемента таблицы, для чего мы добавим столбец, чтобы поместить это значение: | inwricelineltem _ I irwoiceUneltentid j product Id (FK) j ! imeiceM(FK) | qtjantityPurchased | irwoice_customerid : irwotce.flate irwofce_number customer_name I proefactnunTber , | product_name ; I product.price f invo ceLine!tem vaue Здесь дополнительно invoiceLineltem_value — сумма элемента счета-фактуры. Последний шаг включает возвращение к цепочке и суммирование всех потомков. В этом случае мы можем ваять отношение invoice к invoiceLineltem и просуммировать все изделия в счете-фактуре: invoice invoiceld cus'a meric (FK) date titsnber customer name invoiceLineltem nvoiceUneitemid product: d [FKi inwiceMfFK) quantityPurchased invoicejpustomerla involce_date in voice_ni; mber customer_nama prodtictflumber procuct_narrse Здееь дополнительно invoiceLineTtem_valueTotal — общая сумма аммвнтов счета-фактуры.; invoiceI,ineltem_numberLineltema — число элементов счета-фактуры; invoiceLineltem_productCount — общее число изделий элементов счета-фактуры; invoiceLineltem_totalPr-oductSoldValue -— общая стоимость моментов счета-фактуры.
Возьмем, например, craa6en,invo±ceLineltem=valueTotal. Он будет заполнен суммированием всех етолбдов invoiceLineItem_valua (quantityPurchased * productprice) для данного счета-фактуры. Значение invoiceLineItem_number-LineItems выло вы полным числом элементов стета-фактуры. Мы должны сделать то же самое для product, а затем customer, получив следующую диаграмму: product productid number name price i irwoiceLineltem_totaJPnxiuctSol<iCount j invoiceLineltemjotafProductSoldValue irwoiceLinettem irwoicebneltemW ------• prcductld (FK) invoiced (FK) invoice customer customerid name f irwoice_totalValue | ! invoice..totalCount j invoiceM customerid (FK) date number customername inwireUneltem_valuefetal inwiceLineltem_numberlJnelteffis towiceLineltem_proctetCount irwaceLinettem taiaJProductSfl]dWtue quantiryPurchased invoice_customerl d irwoicejdate invoice_number customer_name product_number | , product_name | [ product_price | I irwdceUnettemjaluei '-------- Здесь дополнительно invoiceLi'neItsin_tatalProductSoldCcfunt —общее число проданных изделий в счете-фактуре. inv0ice_tQtal Value — общая сумма счета-фактуры; involce_totalCaur.t — общее количество счета-фактуры. Имеется ряд достоинств этого подхода. □ Простота использования — Так как все связанные данные находятся в одной и той же таблице, построение отчета пользователем или программистом с помощью стандартного средства типа Seagate Crystal Reports относительно просто. □ Распознаваемые данные — Пользователи будут знать имена таблиц и полей в пределах таблицы, тан как мы перешли к большим длинам, чтобы дать нашим объектам имена, которые имели бы смысл для пользователей. □ Полезные итоговые данные — целью является просто попробовать переместить данные вниз по дереву отношений, чтобы устранить объединения. Это дает размещение всех данных, в которых мы будем нуждаться, на одном уровне в реляционной иерархии ВД. □ Относительно легкая реализация — В большинстве случаев мы выполняем простые запросы, которые включают один или большее количество потомков и один или большее количество предков. Это, конечно, не приведет ко всеобщему упрощению, поскольку все будет зависеть во многом от того, насколько сложны ваши структуры ВД.
Например, чтобы загрузить таблицу product, мы могли вы запустить следующий код: INSERT orderReport.dbo.product(productld, number, name, price, lnvolceIrineItem_totalFroductSol<iCou»t, invoiceLineItem_to“al₽roductSoldValue) SELECT product.productld, product.number, product.паше, product.price, SUM (invoiceLine I tern, truant ityPur chased) hS invoic«LineItea_totalProductSoldCount, SQM(product.price * involceLineltwn.quantityFurchased) AS invoicebinelten_totalProductSoldValue FROM dbo.product AS product JOIN dbo.invoiceLineltem. AS invoiceLineltem OH product.productld - invpiqeLinelteB.productld GROUP BY product.productld, product.number, product.name, product.price He каждый ответ будет легко доступен, но мы в основном стараемся обеспечить формирование мрегламентированных отчетов, делая выходные данные более легкими для понимания пользователем. Это возможно, потому что мы удалили всю иерархию из модели данных и скорректировали данные так, как требуется пользователю. Иапримвр, если мы хотим знать общее количество изделий 'XYY43233', на которое был выписан счет-фактура в январе 2001 г, заказчику 10, мы могли бы просто закодировать оператор SELECT из единственной таблицы: SELECT SQM(ix»voicebir»It.em_value> FROM invoiceLlneltee WHERE invoice_custoaerld - IQ AND invoice_date < 'Feb 1 2001' AND invoice^date >- 'Jan 1 2001' MD product_nu«ber = 'XYY43233' С другой стороны, мы могли бы преобразовать данные, предварительно объединив сложные величины, и (раз мы помещаем данные в БД только для чтения) рассмотреть использование представлений, чтобы выполнить любые относительно простыв вычисления. Это, конечно, уменьшит кодирование, необходимое, чтобы сформировать такую БД только для чтения, а также ослабит требования к области данных. Использование представлений таким образом может оказаться более эффективным, чем кодирование, в то время как одновременно допускает большую гибкость, если требования к формированию отчетов изменятся в будущем. Метод, ориентируемый на решение Метод формирования БД, ориентируемый на решение, более легок для объяснения, чем метод, ориентируемый иа таблицы. В некоторых случаях нам просто нужен способ получения некоторых данных быстрее, чем мы можем получить их из нашей GLTP-БД, и мы можем сформировать решение, ориентируемое на извлечение данных, необходимых для этого. Вместо создания полной модели БД, как мы делали ранее, мы могли бы решить обеспечить ответы на очень конкретный набор вопросов. Возьмем, например, отчет о заказчиках и изделиях, купленных ими на прошлых неделе, месяце или годе. Используя ту же самую иллюстрацию ВД иа предыдущего нримера, мы могли вы построить запрос, используя только таблицу invciceLinelcs”, и если мы решим построить полную БД, основанную, как и прежде, на таблице, мы могли 6м создать дополнительную таблицу, чтобы разобраться с атой ситуацией.
Одааяо рассмотрим ситуацию, где запрос занимает сорок минут, чтобы получить данные из исходной OLTP-БД, а коммерческий штат нуждается в этих данных непосредственно с клавиатуры быстро и эффективно. В атом случае мы можем просто построить таблицу, подобную следующей: • customerProduct I customertd i productld ; customer_name : product_name product-price | product_number [ weeKjpanft^₽urchasad [ weeM_*ah«₽urchaseci j mafflh_quanityPurchased ‘ j rnomh_valuePurchased j \ -jearfluanitjPurchased I yearjrtMePurcnased i Wlu.date Здесь cus-tomerProduct — таблица "заказчик-изделие*; customerTd — идентификатор заказчика; productld — идентификатор изделия; customer_naise — имя заказчика; product_name — название изделия; product_pjri.ee — цена изделия; product_number — номер изделия; week_quantity Purchased — проданное количество за неделю; week_valuePurchased — стоимость проданного -за неделю; montbquanci tyPurchased — проданное- количество за месяц; month_valuePurcnased — стоимость проданною за месяц; year_quantityPurchased — проданное количество за год; year=valuePurchased — стоимость проданного за год; build_date — дата формирования. В самом общем случае таблица будет содержать ответ ня их запрос. Здесь мы организовали пересечение таблиц customer я product и обеспечили данные о продажах изделия в течение прошлых недели, месяца и года. Эта модель является гибкой в том, что если требование будет таким, что пользователь захочет просуммировать типы изделии, мы могли бы добавить поле для типа, но вели мы имеем дело с десятками тысяч изделий, вероятно, хорошо будет суммировать изделия по их типам. Мы включаем столбцы идеттификаторов (Id) как первичный ключ, чтобы эта таблица могла бы быть связана с исходной OLTP. В дополнение к этой итоговой таблице пользователю могла бы потребоваться и более детальная информация о заказчике; в этом случае мы могли бы добавить таблицу-потомок, чтобы иметь му информацию. Место, где я наиболее часто использую этот вид итоговых данных, не имеет никакого отношения к продажам, а скорее к содержанию Web-сайта, наподобие обслуживания набора страниц, персонифицированных по типу пользователей. Нормализованные свойства БД, содержащих данные веги, могут быть чрезвычайно уродливыми, однако мы должны обеспечить очень высокий уровень параллелизма при представлении страниц, а также разрешить пользователю делать е OLTP-данными то, что он хочет. Как только содержимое подготовлено, скажем, для общего титульного листа сайта, можно построить версию данных только для чтения, чтобы обеспечить Web-сайт. Здесь мы в результате получаем сильно деиормализоваяную БД, возможно, содержащую одну единственную таблицу. Хотя это и похоже на решение, основанное на таблицах, в этом случае мы имеем меньшее количество таблиц (и также меньший поднабор данных), и каждая из этих таблиц ориентируется на конкретный запрос, а не на описываемый объект.
Реализация Как и с OLTP-системой, мы снова должны рассмотреть реализацию создания нашей ВД только для чтения, на на сей раз мы хотим сделать упреждающее действие, когда не только учитываем функционирование, но и исключительные ситуации и охватываем все и каждые сценарии использования ВД. Так как непротиворечивость ВД только дая чтения управляется подсистемами преобразования, все, о чем нам нужно заботиться — сделать формирование отчетов быстрым. В этом разделе мы рассмотрим две иа наиболее важных проблем для систем только для чтения — аппаратные средства и индексы. Аппаратные средства Поскольку ага тема выла рассмотрена в предыдущей главе, мы только напомним вам о важности не пренебрегать аппаратными средствами при создании системы только для чтения. Может даже быть выгодно иметь отдельный сервер дли размещения данных, в зависимости от объема ваших операций. Вели мы физически располагаем данные только для чтения ла том же самом сервере, ие котором размещается текущая OLTP-БД. одновременно уменьшая содержимое в пределах ВД, мы все же можем разобраться с содержимым аппаратных средств. Даже если вы построили лучшую из возможных -водеистем дисков, вам, вероятно, все же придется купить дополнительный сервер из-за ограничении процессора при одновременной обработке пользователей, запускающих большие отчет», и других пользователей, пытающихся модернизировать основную OLTP-ВД. Индексы При создании БД только для чтения индексы являются неотъемлемой частью проекта, особенно, когда мы допускаем использование нерегламешпированных запросов относительно данных. Так как БД только для чтения будет обновляться нечасто, включение в нее слишком большого числа индексов вряд ли будет главной проблемой. Это, конечно, зависит от фактического размера ВД, таи как если вы имеете терабайты в вашей системе только для чтения, то, что я здесь предлагаю, не может быть реализовано. В среде, предназначенной для нврегламентировашилх запросов, единственной стратегией, которая даст большие преимущества, является создание индексов дия каждого конкретного столбца вашей таблицы. Рассмотрим наш пример таблицы In’/oiceLinelteM, которую мы построили для поиска, ориентированного на таблицы: I ifwofceLinettem I irwotoLineitemH productld (FK) ! irwotteld ФК) ' q па щщ Purchased irwofce^customerId , irwoice_date invofce_number custwner_name prodact_n umber productjwme product.price rwMcetinetenjra'ug Обозначения см. выше) WT
У этой таблицы мы построили вы индексы для всех столбцов индивидуально. Это позволит оптимизатору выбрать ливой или несколько любых индексов таблицы для выполнения запроса, используя неявный запрос. Неявный, запрос — то запрос. когда SQL Server не должен просматривать фактические таблицы., чтобы. получит» требуемые, данные. Так как узлы, индекса содержат данные, которые необходимы. чтобы постраит» индекс, то если мы ссылаемся только на столбцы индекса, или индексов, SQL Server никогда не должен будет читать фактические данные. Обратите внимание, что мы можем даже включать индексы в столбцы, которые в противном случае могло бы покаэатьея глупо индексировать. Возьмем случай Столбца, который содержит род ("М” — мужской, "F" — женский). Он обычно считается ужасным индексом для обнаружения значений. Однако если мы хотим видеть, сколько мужских или женских значений находится в таблице, то можем выполнить неявный- аапрос с помощью индекса рода, и таким образом осуществить доступ только к столбцу рода. Это будет экономить большое количество порядков, потому что такой индекс будет очень небольшим и, конечно, более эффективным, чем использование всех страниц данвых, особенно в расширениях наших таблиц только для чтения. Возьмем, например, запрос доступа к таблице invoiceLinelteni: I SELECT SUM(invoiaeLineItem_value) FROM invoice!,jneltem where invoice^customerld - 10 AND invoice_data •: 'Feb 1 2001' AND invoice_date >- 'Jan 1 2001' AND procfact_nuff.be E - ’XTi43233' В таблице invoiceLineltem мы обратились только к столбцам invoiceLineItem_value, invoice_customerld, inveica_date и product_number. При наличии индекса для каждого столбца мы дали SQL Server возможность самому решить, каков лучший первый шаг, используя статистику индексов, чтобы задать несколько простых вопросов: □ Сколько строк я получу от invoice_customerld = 10? □ Сколько строк возвратится от invoice_date < ' Feb I 2001 ' и invoice_cate > = 'Jan 1 2001 ’? □ Сколько строк я получу от product_nuinber = 'XYY43233'? □ Насколько накладнее будет получить invoiceLineItem_value из таблищд, а не с помощью индексов? Оптимизатор будет не только выбирать лучший план, но также и определит, имеем ли мы один счет-фактуру или миллион между указанными датами, а также и вероятность строк с указанным product_number. Мы не делаем это в нашей OLTP-ВД из-за больших издержек, связанных с поддержанием индексов, но мы считаем, что в большинстве случаев будем делать записи в нашу БД только для чтения, когда никакие пользователи не читают данные.
Время ожидания Время ожидания в этом случае представляет количество времени, которое требуется для части данных, еоадаиной до того, как Судет доступна в нашей ВД. В этом разделе мы рассмотрим факторы, с которыми связано время ожидания нашей ВД только для чтения. Определение частоты обновления Определение, как часто следует обновлять БД только для чтения, зависит от нескольких факторов, которые объясняются в следующем списке. Нам должно быть ясно, что так как имеются ограничения финансов и ресурсов, на практике вы никогда не достигнете идеала мгновенных корректировок. □ Как часто пользователь нуждается в этом — если данные нужны немедленно, то нам, вероятно, придется использовать все остановки, чтобы получить корректировку настолько быстро, насколько возможно. □ Насколько фактически занят OLTP-Server — если сервер используется, чтобы хранить измерения от промышленных роботов или обращаться е заказами в ' реальном масштабе времени, не удастся анализировать данные немедленно из-за нагрузки на сервер ВД. □ Свойства соединений — в зависимости от скорости соединения, конкуренции требований к нашей полосе пропускания и относительной близости OLTP-системы к версии только для чтения, может выть неразумие перемещать массивные количества данных по сети в течение рабочего времени. □ Состояние соединения — когда имеется несколько клиентов, которые получают данные, они могут даже и не иметь возможность соединяться с сервером, кроме как по требованию. Это относится к карманным и портативным ПК пользователей, которые получают копию данных от сервера. □ Размер даиных — количество важных данных в системе изменяется от нескольких записей до терабайт данных. Если мы рассмотрим OLTP-систему с 200 П данных, то в этом случая мы не в состоянии извлечь всю ВД, но смажем, конечно, извлекать и объединять некоторое количество данных для более быстрого использования. Размер данных приводит к неприятному выводу, что чем они больше, тем мы должны выполнить больше дополнительных шагов для построения ВД только для чтения и тем труднее это фактически реализовать, В некоторых случаях мы сможем обеспечить увеличивающуюся корректировку, так как редко требуется каждый раз передавать всю ВД. □ Пригодность ресурсов если вы можете позволить себе только сервер основного уровня, то затрата времени на преобразование OLTP-данных в ВД только для чтения может быть менее эффективной, чем реализация некоторой денормилизации OLTP-даиных в той же самой ВД и создание по мере необходимости хранимых процедур/представлений. Я хотел бы построить для вас матрицу факторов, связанных со временем ожидания, ио ве сделал этого. Не существует- никакой отдельной функции ала набора функций, которые мы можем использовать, чтобы решить, что делать и когда. Взаимодействие между этими факторами сложно, и в каждым проектом нужно обращаться и и дивиду ально при определении частоты обновления, хотя потребности конкретного пользователя должны учитываться всюду, где это возможно, в первую очередь.
Реализация Реализация ВД только для чтения няммого более легкая, чем н случае OLTP-ВД. Мы имеем три средства обслуживания, полностью доступные нам для преобразования данных из OLTP-ВД. □ Сервисы преобразования данных — мощное средство для преобразования данных из одной формы в другую. Оно позволяет использовать в преобразованиях графическое отображение данных, наряду с использованием языков скриптов (VBScript, JavaScript). □ Хранимые процедуры — мы можем написать запросы, которые берут данные в одной форме и помещают их как строки в новую ВД. Для многих случаев это самый простой и самый быстрый способ строить преобразования, особенно, когда они работают между ВД в одном и том же сервере. □ Тиражирование — используется как инструмент, чтобы копировать данные из БД в ВД; действительно удобный способ осуществления преобразования — с помощью хранимой процедуры тиражирования. Таким образом мы можем публиковать одну БД в другую, обходя стандартные средства добавления, обновления и удаления в SQL и используя вместо этого хранимые процедуры. В ВД только для чтения мы можем строить хранимые процедуры, чтобы работать в каждой ситуации, когда изменяется запись или таблица, и соответственно изменять данные в итоговых таблицах. Недостаток этого плаиа состоит в том, что мы должны реализовать инициализацию хранимых процедур, которая может быть довольно хитрой по сравнению с чистыми преобразованиями, когда мы должны добавить или удалить данные из итоговых результатов. В качестве примера рассмотрим текущее общее количество всех сделанных закупок в таблице customer (заказчик), Нам нужно будет закодировать хранимую процедуру INSERT, которая увеличивает значение текущего общего количества. Для процедуры DELETE нам нужно уменьшить значение, а для процедуры обновления UPDATE мы должны будем вычесть предыдущее значение и добавить новое значение. Мы можем таки® использовать тиражирование ж качестве нищего средства распределения, чтобы перераспределить полную ВД всем пользователям. С дополнительной информацией относительно сервисов преобразования данных и тиражирования читатель может познакомиться в "Pro fessla rial SQL Server 2000 DTS " (Wrox Press. ISBN 1Ш004419) и "Professional SQL Server 2000 Programming (WroxPrese, ISBN 1861004486). С каждым из этих средств вы можете сформировать получение данных, удовлетворяющее разнообразным потребностям. Давайте рассмотрим ситуацию, когда мы имеем организацию по обработке заказов в Нэшвилле, а штаб-квартиру в Нью-Йорке. В то время как заказы поступают и на них выписываются счета-фактуры в Нашвилле, общая штаб-квартира, куда поступают отчеты, суммирующие продажи, расположена в Нью-Йорке. Так как штаб-квартира в Нью-Йорке не имеет достаточно места, чтобы разместить любой из первичных общих компьютеров, разработан следующий проект:
В свою очередь мы имеем три различных проблемы, с которыми приходится иметь делох □ Преобразование данных (А> — Мы должны взять нормализованные данные и сформировать из них нужные данные. □ Размещение данных на легких клиентах (В) — Это вряд ли разместит все д анн ые на клиенте; скорее всего это небольшой, очень определенный навар данных, который даст возможность польааваташо делать его работу лучше. □ Загрузка данных е одного физического объекта на другой (С) — С нашим бизнесом, становящимся все болте глобализованным, мы должны быть способны транспортировать данные по сети. Теперь рассмотрим каждую из этих проблем отдельно. Преобразование данных Преобразование данных из исходного нормализованного формата имеет два возможных направления: □ Очистить и начать снова — Всегда, когда спадается ВД только для чтения, мы должны создать метод первоначальной загрузки ВД. Вели требуются =--, преобразования, мы должны будем синхронизировать БД с текущим состоянием OLTP-ВД. Вели размер данных разумно невелик, и имеется период времени, когда использование ВД несущественно, мы можем просто удалить данные и начать снова. Это — самый легкий метод, когда вы должны обеспечивать ежедневное " получение информации. □ Использование в транзакции процедур тиражирования — Если нам требуется ? обновлять данные часто, или данные слишком большие, чтобы работать, очищая их и синхронизируя, мы можем написать процедуры тиражирования, чтобы поддерживать данные в ВД.
В качестве примера закодируем процедуру из нашего примера с четырьмя таблицами и посмотрим, что мы должны будем делать, когда помещена новая запись в таблицу invoiceLineltem. Мы должны будем не только поместить новую строку в invoiceLxnelter но также и скорректировать таблицу product, как. и другие таблицы в цепи отношений: CREATE PROCEDURE involceLlaelteeSreplInsert ( einvoieeLineltemld int, eproduculd Inc, ginvoiceld int, 0quantity₽ucchased int ) AS — транзакция и овработка ошибок ив помеввнм для ясности INSERT INTO invoicebineltem (invqiceLineltemld, productld, invoiceld, quantityPurchased, involce_customerld !, invoice_date, invcice_mnnber, customer name, product—number, productname, product price, invoiceLineItem_value) SELECT ginvoiceLineltemID, SproduetID, ginvoicelD, @miantitypurchased, invoice.cuatomerlD, invoice.date, invoice.number, customer.name, product.number, product.name, product,price, invoice.quantityPurchased * equantltyPurchased FROM invoice JOIN customer ON customer.customerld - invoice.customerId ON «invoiceId - invoice.invoiceId -— CROSS JOIN используется, нагому что в. обычном запрос* мы вы о&ьедаияли — эти два набора для таолицы invoiceLineltem, которая представлена - переменными величинами, гэран-ирувщимн декартово произведение двух — отдельных строк, которые воэвраиают одну строку CROSS JOIN product ON product.productld - Sproductld UPDATE product -- довавление купленного количества к количеству пролаиных изделий SET invoiceLineItem_totaXPrcductSoldCcunt invoicebineltent—totaiFroductSoldCount + gquantltyPurchased, — добавление купленного количества, умноженного на цену для значения общей стоимости invoiceLineItem_totalProductSoldValue - invoiceLlneItem_tot.al₽roductSoldValue + price * equantltyPurchased WHERE productld -'fproductld — аналогичные'типы операций волны выть для таблиц invoice и customer GO Обратите внимание, что мы не будем делать никакого суммирования, так как в это время только вставляем строку. Однако нужно учесть количества, которые были добавлены. Обычно это выполняется довольно быстро. К сожалению, все это иожет быть более
дорогостоящим, чем удаление всех данных и получение нх снова для набора данных, подверженных постоянному изменению (они обычно меньше по своей природе). Может быть лучше попробовать просто удалить данные и добавить их снова, так как это требует намного меньше усилий от нас. Размещение данных на мобильных клиентах Мобильные клиенты — работающие или с портативными, или кирмянными ПК. Лучший метод реализации — "по требованию" клиента при работе с разъединенными клиентами состоит в том, чтобы реализовать, «ишмнску тиражировании (так как мы ие можем контролировать, когда и где они будут подключаться). Это позволяет клиентам подключаться каждый раз, когда они захотят, и почти так же, как почтовая программа обращается к серверу для получения электронной почты; SQL Server на клиенте вызывает главный сервер и запрашивает любые изменения. Этот тип тиражирования может также использоваться в случаях, когда мы должны обновить строки в других таблицах. Загрузка данных с одного физического объекта на другой В этом случае тиражирование с помощью транзакции (где каждая транзакция, которая применяется к одной ВД, будет использоваться для тиражирования) было бы способом решения с таким большим временем ожидания, которое может позволить клиент, или, по крайней мере, настроенное прежде всего на выполнение, когда нагрузка на сервере самая низкая. Обратите внимание, что любой тип тиражирования использует загрузку с помощью транзакций, чтобы обеспечить синхронизацию, так что если вы должны сделать много изменений в любой из БД и хотите скопировать их, загрузка транзакции будет продолжать выполняться, пока агент по распределению или объединению не будет способен использовать все загрузки для нужной БД, Использование Мы имеем здесь удобный случай, чтобы кратко перечислить несколько возможных использований ВД только для чтения; □ Простые потребности формирования отчетов — здесь просто обращаются к отчетам для отдельных ВД, □ Оперативное хранилище данных — ВД, где мы объединяем данные из всех наших внешних источников. □ Web-еайты — так как большинство содержимого, поетупаемого от Web-сайта, используется только для чтения, любое содержимое, в котором нуждаются пользователи, может быть преобразовано из нормализованных структур в формат, который организован в соответствии е любым конкретным сайтом. - □ Портативные компьютеры — мг- могли бы пожелать учесть пользователей портативных компьютеров (которые могут иметь копии данных о продаже или даже экспертные таблицы) так, чтобы они имели мгновенный доступ к самым последаим данным, Очевидно, здесь нет границ. Отделяя серьезных пользователей, которые читают данные от серьезных пользователей, которые записывают данные, мы даем обеим группам пользователей огромное преимущество в работе. Теперь поглубже посмотрим на пару / использований БД только для чтения.
Задачи простого формирования отчетов Обратите внимание, что когда я говорю "щюстой”, то не отношу вто ни к запросам, которые мы можем поддерживать, ни к сложности отчетов, а скорее к работе, которую нужно выполнить дли настройки формирования отчетов. Обычно мы создаем наши БД для формирования отчетов из единственной ВД. Это довольно общее использование ВД только для чтения, так кар, выполнив ли:пн небольшую работу, мы можем дата пользователю то, что ему нужно для ответа яа вопросы вез опасения случайного изменения чего-нибудь в иеходаой системе. Обычно в этом случав iax можем иметь два нуги. Мы можем обеспечивать конкретные отчеты, которые нужны пользователю, подобно тому, как мы ранее поступали с таблицей cuercnerPr octet: [ SrwoteeLlneften» йжисШпекетИ produciki (FK) irwotcsld (FK) quantttyPurchasetf iiwoice_customerid irweioe_date iKOice_numoer customarjiame praduct_number pradacLname ; productjjrice | kwdceuneltemjaiue} (Обозначения ем. выше J Мы делаем это, удаляя все соединения и давая пользователям отчет, у которого они могут настраивать параметры. Так как большинство отчетов основано на общем результирующем наборе информации, такой путь на самом деле работает хорошо для мало осведомленных клиентов с очень конкретными потребностями. Однако если мы имеем более осведомленных клиентов, то № могли бы реализовать что-то более близкое к решению, ориентируемому на таблицы, которое мы раньше разработали. English Query Средство для формирования нлрезламектированных отчетов, о котором, я уверен, вы слышали, называется English Query (запрос яа английском языке). Это — продукт, который поставляется с SQL Server (начиная с версии в.в) и используется, чтобы применить интерфейс на естественном языке для ВД на SQL, перевода вопрос типа: Каково было общее количество стираемого ППЗУ (программируемое постоянное запоминающее устройство), на которое были выписаны счета-фактуры Зля компании "Bob’s electronics" в прошлом году) ... в привычный для SQL вызов данных из нашей таблицы выше: SELECT SUM (invoicebinelteei-Valtte) FROM InvoicaLineltem WHERE product_name - * X’ and sustomer_nanie • ’Bob''s electronics' AND invoice_date >» ‘ jan 1, 2001' AND invoice_date < 'jan 1, 2002'
Конечно, это может также работать и в OLTP-БД, и создаст следующий запрос: SELECT SUM(involce.q»»ntityP«zchased * fquant-ityPurcbased) FROM- invoiceLLaelcen JOIN invoice JOIN customer ON customer.customerId ~ invoice.customerld ON invaicebiaelt-ея/inTOieeld. - invoice.invoiceld JOIN product ON у rodutt. t rcdactld - invo-icebinelteauprodactld WHERE ргойцвн.name ~ AND customer .name - 'Bob"s electronics' AHO invoice.date >= 'Jan I, 2001' ANO Invoice.daw < ' tan. X, 2002' Если это выглядит как волшебство, то это не так. Имеется очень детальный семантический формат моделирования (который является XML-языком), который мы должны использовать для размещения "модели" ВД с описателями для каждой таблицы, атрибута и отношений, так что общее количество и выли выписаны счета-фактуры. в нашем вопросе могут выть преобразованы я оба из вышеупомянутых запросов в различных БД. Мы были бы, конечно, должны смоделировать всю ВД, для которой хотели бы распознавать запрос на английском языке, чтобы можно было выполнить эти запросы. Дополнительную информацию относительно English Query читатель может получить в "Professional SQL Server 2000 Programming" (Wrox Pres», ISBN 1861004486). Оперативное хранилище данных Это — БД, где мы берем все данные из любых (и, возможно, всех) наших ВД, как показано на следующей диаграмме:
Это — БД, где мы объединяем данные из всех наших внешних источников данных. В оперативном хранилище данных мы будем использовать многие методы нормализации (а для формирования отчетов — некоторую денормализацию), которые мы обычно использовали при создании нашей OLTP-БД, чтобы сформировать довольно современный набор данных. Это позволит нам принимать решения, которые дадут возможность обеспечивать всю деловую организацию более эффективно. Оперативное хранилище данных будет содержать модель, которая является в гаюштельной степени подобной нягняи OLTP-ВД, с некоторой степень» денор'.'а.тизащги, Цель не состоит в том, чтобы построить одно оперативное хранилище данных для каждой OLTP-БД, скорее оно долаво объединить организационные данные из всех общих источников в общее хранилище, полностью очищенное и с общими данными, согласованием со всеми другими данными. Например, пусть мы имеем две БД, одау — внешнюю и одну — собственной разработки, причем каждая из ВД используется своей организационной единицей, Неудивительно, что каждая БД заполняет таблицу vendor (продавец) продавцами, работающими в каждой организационной еданице. Первый шаг, который свяжет сделать, — смоделировать каждую из исходных ВД, используя общие термины. Например, мы можем иметь две БД, которые моделируют продавцов совершенно по-другому- Или: vendor________ vendorNuraber vendorName aedressl address2 City state tip Здесь vendor — таблица "продавец"; vendorNuatber — номер продавца; vendorName — имя продавца; addressl — первый адрес; address2 — второй адрес; aity — город; state — штат; zip — почтовый индекс. Или: vendor vendorld name vendo-Acdress 'addressM (FK) * vendoria (FK) aod-essTypeld (FK); address addresstd _ ecitytcf (FK) statetd (FKJ zipcodeld (FK) 1—•—i—“ addresstine addressTjpe addressTyeld name [ description zipcode । zipcodeM i zipcode j ade'essid (FK) adcresslirsP address .sortOrterki Здесь vendor — таблица "продавец"; vendorld — идентификатор продавца; name — имя; vendorAddress — таблица "адрес продавца"; addressTypeld — идентификатор типа
адреса; address — таблица "адрес"; address'd — идентификатор адреса; addressLine— таблица "строка адреса"; aMressLineld — идентификатор строки адреса: sorcOrdecId— идентификатор порядка сортировки; addressType — таблица "тип адреса"; name — название (типа адреса); description — описание типа адреса; zipCode — таблица "почтовый, индекс"; zipCodeld — идентификатор почтового индекса; city — таблица "город"; cltyld — идентификатор города; пате — название (города); state — таблица "штат"; statelet — идентификатор штата; code — код штата. В случае этих таблиц выбранным путем было Хна, вероятно, построение нашей модели оперативного хранилища данных, используя нормализованные структуры и преобразовывая данные первой структуры .во вторую. Мучительно трудная часть создания оперативного хранилища данных — слияние существующих ВД. Возвращаясь к первой главе, мы получим следующую диаграмму: Все потоки даиных через этот очищающий цикл извлечения, преобразования и загрузки, чтобы получить данные от каждой ВД, должны совпадать с соответствующими данными от других ВД. В заключительном оперативном хранилище данных мы хотим хранить данные в одной и той же манере для каждого их варианта; так, если мы имеем десять таблиц продавцов в десяти различных ВД (или электронных таблицах, или любых используемых хранилищах данных) и мы имеем пять различных записей названия 'Joe’s Fish Market' (рыбный рынок Джо) — ’Joe’s Fish Market’, ’joes phish mark;', 'joe fish' и 'joe market' — которые все представляют один и тот же рыбный рынок, мы объединяем содержимое всех этих строк в одну н ту же строку в нашем оперативном хранилище даиных. Очевидно, я не дал ваи достаточной информации, чтобы построить полное оперативное хранилище данных; скорее я попробовал дать достаточную информацию для читателя, чтобы начать размышлять о том, как начать процесс создания хранилища.
Модели данных предприятия Концепция — относительно понятная. Модель данных предприятия — это модель, которая покажет нам каждую таблицу и все отношения в каждой БД, с которой мы имеем дело. Таким обрезом, она позволяет архитектору иметь полное представление всех данных, которые использует корпорация. Звучит вто хорошо, К сожалению, было бы удивительно, если можно выло бы получить совершенную модель данных предприятия. В нашей учебном примере, который мы строили на протяжении книги, имеется почти двадцать таблиц для того, чтобы выполнить очень небольшое-чяшго функций. Теперь рассмотрим корпорацию практически любого размера и более сложной банковской системы, содержащую, например, около двух сотен таблиц. Затем заметим, что большинство корпораций имеет больше одной ВД, которую они создали, и еще несколько систем управления, которые они приобрели и которые во многих муиях требуют интерфейсов от других систем. Это может привести нас к системе, показанной схематично в следующей диаграмме: Затемненные прямоугольяшси обозначают системы, которые были куплены, а линии представляют имеющийся интерфейс. Каждая из атих_ сиетем будет требовать сотен таблиц для своих целей, и мы, вероятно, создадим несколько таблиц, чтобы поддержать все связи между ними. Цель модели предприятия состоит в том, чтобы соединить все модели в одну "великую модель", где мы можем вадеть все взаимодействия и получить ясную картину того, что мы имеем и как ови взаимодействуют. Это в любом случае непростая задача по нескольким причинам: □ Абсолютный объем данных — Предположим, что мт пример выше содержит одну тысячу таблиц, □ Закрытый характер систем третьих лиц — Даже в ряде лучших средств третьих лиц разработчики стараются скрыть детали реализации своих систем от любопытных глаз. Деловые обстоятельства таких схем скрыты, ото делает поиимание/использование данных почти невозможным. □ Разные типы серверов ВД — сочетание SQL Server 4.2, в.б, Т.0, 2000; Oracle б, 7, 8; Informix и т, д. затрудняет нас разовраться, что происходит на сервере. □ Изменения в модели — Сохранение модели данных, синхронизированной с единственной БД, — привлекательная задача, не требующая строгих правил того, кто может изменять модель. □ Стоимость — учет всех этих пунктов может быть весьма дорог.
Мы можем избежать некоторых из этих проблем, решая просто реализовать логическую модель предприятия, весьма возможно, удаляя из рассмотрения закрытость системы, и включая только таблицы интерфейса (иля те, которые мы создаем для взаимодействия с процессами, или фактические таблицы системы, которые нас интересуют). Это приводит нас к совершенно другой проблеме, а именно, обеспечить, чтобы логическая модель ВД соответствовала ее физической модели. Следующая сложность состоит в. том, что некоторые из систем, которые мы создадим, могут включать многие "модули" исходной системы, типа системы бухгалтерского учета: Цель любого раздела этой книга состоит в том, чтобы выделить оитуацию, описать проблему и затем предложить, что делать в агой ситуации. В данном случае ответ не преет. Единственный способ обеспечить синхронность логической модели с физической моделью и, что более важно, с физической реализацией, — быть дисциплинированным» Единственный путь — это взять меаьшие модели и построить модель предприятия — снова подчиняясь дисциплине. Установленные на теоретической стадии процессы должны быть заменены кодом на стадии построения. Изменения в схеме БД должны протекать в определенном порядке, типа: □ Создать иди изменять логическую модель, чтобы она содержала все данные, которые в настоящее время требуется хранить. □ Модернизировать физическую модель, чтобы отразить эти изменения принятым образом. □ Изменить физическую структуру. □ Модернизировать модель предприятия, чтобы отразить текущее состояние системы каждый раз, когда что-нибудь изменяется. Как фактически преобразовать цель модели предприятия в действительность, будет во многом зависеть от того, какие средства вы используете для моделирования ваших данных и предприятия. Важно подобрать правильные средства и хранилища, которые позволят вам построить модель и педдерясиввть ее через какое-то время, когда ивменетея ваши условия. Переход от опытного варианта При разработке программного обеспечения мы должны выть реалистичными. Наш проект никогда не будет совершенен и никогда не будет окончательно закодирован. Вели бы было так, мы могли бы сначала разработать новое программное обеспечение, затем вручить его пользователям и перейти к следующему проекту. Вторая действительность заключается в
том, что как только пользователи будут иметь возможность работать с программным обеспечением, им потребуется внесение изменений. Обычно мы используем дм развития итерационный процесс, включающий три основный стадии: □ разработка; □ проверка характеристик; □ реализация. Каждая среда играет фундаментальную роль в процессе создания кода, его проверки и использования персоналом. Цель состоит в том, чтобы иметь процесс, где наш код проходит через ряд стадий, которые выглядят следующим образом: Кодирование начинается на стадии разработки ВД, затем перемещается на стадию проверки характеристик н, наконец, на стадию реализации. Хотя мы н стремимся к такой ситуации, она, конечно, не достижима. Практически невозможно разработать достаточно сложную систему и не сделать каких-либо ошибок. Обычно, как минимум, процесс выглядит так; А на самом деле ма могли бы нарисовать линии от каждой стадии ВД к каждой другой стадии. Однако когда дефекты выявлены на стадии реализации, мы должны опять пройти этот же цикл разрабатываемого кода, проверяя его качество и, наконец, возвращаясь на стадию реализации. В дополнение к этой модели е тремя стадиями, обычно имеется стадия опытного образца между стадией проверки характеристик и стадией реализации. Мы можем также иметь две или больше ВД на стадии проверки характеристик для различных типов испытания (испытание нагрузки, проверка требований пользователя, проверка определения правильности всех алгоритмов), которые опять-таки основаны на разрабатываемой системе и требованиях к ней. Некоторым организациям могут потребоваться различные варианты разработки, чтобы обеспечить различные версии кода для поддержания системы на стадии реализации, одну из которых пользователи будут применять в настоящее время, и другую — для новой версии.
Однако мы рассмотрим все это в минимальном объеме, так как независимо от того, сколько вы фактически имеете систем, будут одни и те же проблемы. Разработка Она представляет совой среду, где мы будем создавать наши БД, с которыми начнется работа (и что мы до сих пор предполагали в тексте книге). На этой «ащккы не должны иметь никаких пользователей или испытателей, а только архитекторов, администраторов БД, программистов пользивательексго интерфейса, кодировщиков ВД и т. д., анализирующих и создающих новый код. Аппаратные средства разработки Аппаратные средства для разработки должны выть минимальны, но разумны. Если ваш испытательный сервер столь же мощен, как ваши серверы БД иа стадии реализации, разработчики будут стараться использовать каждую его часть, чтобы решить задачу, и будет очень трудно обнаружить, какие у вас будут проблемы с настройками данных, когда перейдете к стадии реализации, То же самое можно сказать относительно полосы пропускания сети. Если вы создаете приложение для глобальной сети с 2-х мегабитовым соединением между клиентом и сервером, а строите ваши приложения иа 100-мегабитовой сети, может быть трудно выбрать надлежащее решение. Имейте в виду, однако, что сервер ие должен быть столь медленным, чтобы препятствовать процессу, Выбор надлежащего баланса для среды разработки — трудная «дача, и, вероятно, лучше допустить ошибку на ©тороне слишком больших аппаратных средств. Это решение должно также быть умерено знанием того, что большинство испытательных серверов обычно не используется только для одной СУБД, а используется для полного- обееиечевия БД, включая разработку нового программного обеспечения и испытания новых версий коммерческого программного обеспечения. Проверка характеристик Как только программное обеспечение (или части программного обеспечения) будет доведено до уровня, когда можно считать, что оно готово для передачи пользователям, оно должно быть проверено. Среда проверки характеристик используется для доказательства выполнения основных требований несколькими способами: □ Испытание характеристик — Позволяет проверить, что программное обеспечение (ВД и код для доступа к ВД, наподобие пользовательского интерфейса) выполняет то, что рекламируют программисты, а также проверить, что система выполняет требования пользователей. Оно будет включать проверку правильности работы системы (что все алгоритмы правильны), проверку безопасности и, особенно, что каждая часть ее работает. □ Испытание нагрузки — Используется, чтобы поместить программное обеспечение в реальные (а во многих случаях и сверх реальных) условия загрузки, чтобы посмотреть, может ли оно работать с нагрузками, с которыми будет работать на стадии реализации, □ Приемочные испытания — Прежде, чем пользователь сможет начать использовать новую БД или потребуется внести некоторые изменения в нее, пользователи, как правило, должны одобрить написанное программное обеспечение, чтобы согласиться с тем, что оно делает все, что было в исходной спецификации.
Аппаратные средства проверки характеристик Ключ к правильному уровню аппаратных средств при проверке характеристик — масштабирование. Не каждая организация будет способна полностью дублировать аппаратные средства своих серверов, поскольку сервер(ы) может (могут) составлять большой процент от полного бюджета разрабатываемой системы. Однако рассмотрим это здесь. Если вся система проверки характеристик составляет 10 процентов мощности от стадии реализации, мы будем в состоянии работать в 10 рна активнее с системой иа стадии реализации. Обратите внимание, что, конечно, мы редко будем способны масштабировать каждую часть нашей системы на одном уровне, во хороший администратор должен быть способен аппроксимировать процесс и получать приемлемый коэффициент масштабирования для испытания. Минимальные аппаратные средства требуются для другой задачи проверки характеристик, — удостовериться, что все разработанное выполняет заданные спецификации. Во время таких проверок ускоренные испытания аппаратных средств будут полностью удовлетворять текущим сценариям испытаний, которые определяют входы для процеесов/процедур и осуществляют передачу надлежащи значений, а также исиытажвям приложения. Реализация Среда реализации — это то, когда пользователь фактически использует БД, Как только мы пропустили наш код со стадии разработки через процесс проверки характеристик, теоретически он должен быть свободен от ошибок (конечно, иа гфактике обычно требуется несколько циклов разработки и проверки характеристик, чтобы получить, результат, практически свободный от ошибок). Хотя формирование ВД на стадии реализации не является задачей архитектора данных, исходя иа моего опыта, архитектор данных часто привлекается для определения того, как эта система должна использоваться и поддерживаться (особенно в небольших организациях), и может в большой мере отвечать за недостатки в ВД и аппаратных средствах. В главе 13 мы дали основное представление о некоторых проблемах аппаратных средств, с которыми вы столкнетесь, а также о некоторых формул ах/хранимых процедурах, которые мы можем использовать, чтобы помочь с проблемами калиброзки ВД. Мы также вычислили размеры (и проекты будущих размеров) данных, которые будут размещены в нашей ВД. В большинстве случаев это будет полностью работой администратора БД, ио важно, чтобы вы понимали основы, так как не все администраторы ВД полностью разбираются в аппаратвых средствах, а во многих случаях может даже и ве быть специальиото администратор.а ВД, так что вам, вероятно, придется участвовать в формировании требований к аппаратным средствам для СУВД. Обслуживание и планы восстановления поломок Хотя мы и не будем рассматривать, как поддерживать БД, иы должны обсудить несколько важных моментов, чтобы рассмотреть, как система переходит к стадии реализации. Обычно этот вид информации, вероятнее всего, определяется, когда архитектор данных почти закончил свою работу. Здесь имеются два важных момента: обслуживаниа и восстановление поломок, которые отличаются друг от друга.
И планируя обслуживание, и планируя восстановления поломок, юг ограждаем себя от простых проблем, связанных с программными средствами и программным обеспечением, наподобие отказов системы бесперебойного питания (обычно, резервная батарея) и аварии жесткого диска. Это довольно ясные проблемы, если администратор БД понимает некоторые моменты: □ Размер данных — Используя формулы, рассмотренные в предыдущей главе, мы можем получить прогноз размера данных иа основе их ожидаемого роста, который был оценен в конце логической стадии проектирования. □ Приемлемая потеря данных — Это легко! Никаких потерь. Конечно, это — не подходящий ответ, поскольку мы должны принять, что будем всегда, иметь некоторый, промежуток времени, когда, имеется потеря данных из-за массового отказа системы. Однако когда время допустимого риска становится меньше, стоимость этого увеличивается по экспоненте. Если мы можем потерять данные дня, мы просто должны обеспечить каждую ночь некоторое сохранение данных на другой машине. Если час — приемлемая потеря, мы можем делать ту же. самую вещь с загрузками транзакций, я то время как пользователи используют данные, и мы можем устранить почти любую потерю данных, используя кластерные методы (всегда имеется некоторое минимальное время ожидания, которого нельзя избежать). Конечно, выделение кластеров гораздо более дорогое, чей привод ленте, и с выделением кластеров вы все еще нуждаетесь в приводе ленты. Стоимость, конечно, является основной характеристикой для клиента и менеджера проекта. □ Приемлемый уровень обслуживания — Это было бы вообще некоторым признаком ожидаемой нагрузки в рваное время в -течение дня. Оя также детализировал бы ожидаемое/обговорвнное время работы, когда ВД должна быть активна. Конечно, это звучит наподобие пункта Приемлемая потеря ваммых^тюшаллкр ожидаемый уровень обслуживания — всеобъемлющий, как всегда хочет клиент; но это -— нереально и никогда не будет оговорено таким образом. Чтобы полностью обеспечить это требование, мы должны также знать, как долго будет восстанавливаться сервер. Для плана восстановления поломок мы должны учесть "реальные" проблемы, типа ураганов, пожаров, торнадо, и даже унесенного трансформатора из соседнего здания. Паяный план был бы просто расширением плана обслуживания. но ои будет зависеть также от . следующего: □ Отказы витания — Отказы питания — очень серьезная ситуация. Сколько раз мы слышали истории относительно подключенного розничного продавца, который отключился яа несколько часов? Это нетрудно распознать. Наличие генератора, который будет запущен задолго до отказа бесперебойного питания, является лучшим способом быть уверенными, что вы ве будете иметь никаких перерывов в работе, связанных с электропитанием. □ Природные бедствии — Что, если все отключится из-за урагана? Или торнадо? Или даже наводнения? В глобальной экономике, если ваш сервер отключится иа неделю, вы попадете, вероятно, в ночные новости (или хуже -— в финансовые новости) и, возможно, поставите в затруднительное положение вашу компанию. Вы будете, вероятно, должны компенсировать это использованием городских серверов.
□ Другие бедствия — Бедствия принимают различные формы и раамеры, и мы даже не мажем их все перечислить. Ошибки в электропроводке, плохо настроенные системы, мыши, насекомые, извержения вулканов, или (к сожалению) даже неудачно запланированная модернизация программного обееиечеимя — все может привести к нарушению работы системы в течение недопустимого промежутка времени, Последаяя я, конечно, наиболее важная проблема, связанная с обслуживанием и планированием восстановления поломок — тестирование системы. Лучшие разработанные планы — от мышей и администраторов ВД ... или кое-что вроде итого. Вы можете строить наиболее внушительный план восстановления поломок с наиболее впечатляющим генератором и бесперебойным питанием, но если вы запасли топлива, чтобы управлять генераторами только влечение дня; а если впоследствии возникнет ледяной шторм, который отключит анергию на два дня, и у вас нет возможности получить топливо, то ваш план будет в какой-то мере неэффективен. Таким же образом, если вы выдернете штепсель основного бесперебойного питания только для того, чтобы обнаружить, что не можете запустить генератор перед тем, как ваше бесперебойное питание будет отключено от энергии, то, короче говоря, ох, что произойдет! Учебный пример В этом заключительном учебном примере мы столкнемся с решением одной последаей оставшейся задачи создания ВД, формирующей отчеты. Вели бы мы действительно строили игу систему, то, вероятно, на самом деле на создавали ВД, формирующую отчеты, поскольку наша система маленькая с невысокими требованиями параллелизма. Однако для законченности мы создадим тавлиду, чтобы поддержать конкретный отчет, который мы выбрали в разделе учебного примера из главы 8. Ои выло описав следующим образом: Активность пользователя — Список всех сделок, связанных с данным пользователем, или просто сгруппированный по пользователям. Также нужно иметь возможность показать любые сделки, не связанные с пользователем, который фактически потратил деньги. Следовательно, мы должны веять таблицы, связанные со сделкой, а именно: □ transaction (сделка) — непосредственно сделка: □ account (счет) — счет, от которого поступил чек; Q bank (банк) — банк, где находится счет; Q payee (получатель платежа) — для чеков и, возможно, других операций, основанных на таблице transactionType; □ user (пользователь) — список пользователей, известных системе, которая выписывает чеки; □ transactionType (тип сделки) — классифицирует тип сделки; □ transactionReconcile (согласование счета) — если запись существует в этой таблице для сделки, она должна быть согласована.
Отношение между ними, полученное в итоге, имеет следующий вид: Мы сведем многие и» полей этих таблиц в единственную таблицу следующим образом: transactionRoilup йамвоВопИ: inttOENTTTY date: smallDateTime NOT NULL number, varchar! 20) NOT MULL description: varcharf 1000) NOT NULL amount money NOT NULL sig nature: wchar{100) NOT NULL reccnchedF!: bit NOT NULL payee^name: varchar (20) NOT NULL user_usertianw: sysname NOT NULL user prstName: varchar(20) NOT NULL - user middiename: varchar(20) not null userJastName: varcha020) NOT NULL ban<name: varchar) 20) NOT NULL accDunt_mmber. varchar (20) NOT NULL t'ansactionType_name: /archart 20 j NOT МЯХ Здесь transactionKollup — таблица "объединенная сделка"; transactions — идентификатор сделки; date — дата (сделки); number — номер (сделки); description — описание; amount — сумма; signature — подпись; гесапсИедП — флаг согласования; payee_name — имя получателя платежа: user_uaarlf№e — системное имя пользователя: user_firstUame — имя пользователя; user_middle№at№ — отчество пользователя; user_lastName — фамилия пользователя; bank_naoe — название банка; account_number— номер счета; transact 1опТуре_паше — имя типа транзакции.
Так как нашей системе не требуется сложный доступ, мы просто создадим хранимую процедуру, чтобы усечь даяние и таблице transactionRollup и заменить их надлежащими значениями. Вместо перемещения этой таблицы к другой ВД мы поместим ее в ту же самую БД и никогда не будем позволять пользователя’.: изменять ее. Для меньших систем это позволит вам разрешить пользователям получение данных только для чтения без необходимости создавать дополнительную ВД. В следующем коде мы создадим процедуру преобразования. Хотя она и довольно велика, но, е другой стороны, и довольно проста. Процедура обеспечивает все соединения е таблицами-предками таблицы transaction: CREATE PROCEDURE transactionRol lupStransfo» AB — овммевие пеженнкх, DECIARE вrowcount int, gerror int, используемых иля. управления сшивками в блоках -- содержит число строк, возвращаемых вызовом ЯМД — используется для размещения кода ошибки — после вызова ЯМЖ. l“sg varchar(255!, — используется для предварительного -- форматирования сообщений со ммбках Iretval int, — общая переменная для восстановления -- возвращаемых значений ат вызовов -- хранюмх процедур etranName sysname, SmsgTag varchar (25S} — используется для размещения имени транзакции — используется для размещения тега сообщения об ошибке — задание тега соовшения ов оемйгл SET (JjasaTag '<' * object__na» (eiprocld} * ’.-type-P' + r; keyvalue*»' + ' gkey_bankld: ’ + convert (varchar <10>, ejcey_bankld) SET fitranName - object_na»e (Mprocld) t CAST (@ Sites tlevel AS varchar(2}} BEGIN TRANSACTION SAVE TRANSACTION gtranName TRUNCATE TABLE transactionRollup IF Merror 1=0 — ошибка возникла за пределами эвой процедуры BEGIN SELECT Smsg 'Проблема возникла при отсекании тавлииы transactionRollup' + SmsgTag - ,'са11-<отсвканив transactionRollup!>' ROLLBACK TRANSACTION вСгапЫатв COMMIT TRANSACTION RAISERROR 50000 fimsg RETURN -100 END INSERT INTO trans-actionRollupttransactienld, date, number, description, amount, signature, reeonailedFl, payee_name, ивег_ивегНаяе, usar_firstKame, user_middleNatae, user_lastHa«e. bank__name, accoiint_ntssber, transactionType_name) SELECT «action,transactionld, xaotion.date, xaction.nuBiber, xaction.description, xaction,amount, xaction.signature,
- - есж мним соединение с таблицей transactionReconcile - - допускает значение BOLL, соединение тоже Судет BOLL. — Следовательно, если ид проверяем на NULL это поле, — то можем ушаи>, согласована ли запись CASE WHEN transactionR.ecancile,transactionReconcileId IS NULL THEN 1 ELSE 0 END BS reconclledFl, payee.name, user.userName, user.firetName, user.middleName, bank.name, - account .number, txanaactionlype.name FROM dbo.[transaction] AS xaction — получение счета и банка JOIN Ло. account AS account JOIN Ло. bank, AS bank . ON bank.bankid = account.bankid ON xaction.aaaountld - account.accountId — получение типа сделки JOIN dbo.transactionType AS transactionType ON trensactionType.transactionTypeld - xaction.transactionTypeId — не все сдеми суду- ;<меть получателя платежа LEFT OUTER JO IK fc, payee AS-payee ON payee.payeeld - xaction.payeeld — не все сделки инея связанных с ними пользователей LEFT OUTER JOIS dfao.user AS user ON user.userid - xaction.userId — левое внаинее соединение в случае, если значение не согласовано LEFT OUTER JOIN Ло.transacticnRecencile AS transactionReconcile ON transactionReconcile.transactionld - xaction.transactionld IF ggerror !- 0 — ошибка возникла за пределами этой процедуры BEGIN SELECT gmsg - 'Проблема возникла при отсекании таблицы transactionRollup' + gmsglag + 'cail=(отсекание transactionRollup)>' ROLLBACK TRANSACTION BtranName C0M4IT TRANSACTION RAISBRROR 50C00 gmsg RETURN -100 END COMMIT TRANSACTION Конечно, мы должны перечислить и другие задачи типа установки среды реализации, настройки функционирования и т. д, Однако эти задачи за пределами возможностей данного учебного примера и были упомянуты в большей мере для того, чтобы обратить внимание на их существование для случаев, когда мы вызываем помощь для определения аппаратных средств, типа случая, когда штат администраторов ВД всего земного шара запускает новые операции в действующем Интернете. Понимание того, как все работает, даже если мы никогда не должны нажимать кнопки или движки SQL Server, является очень важным.
Резюме В згой главе мм рассмотрели некоторые из проблем, которые могут возникнуть в конце проектирования, а также связанные с приемлемым представлением результатов и переходам к следующей работе. Мы начали с обсуждения потребности в настройке функционирования, и рекомендовали строить запросы и настраивать работу после проектирования, SQL Server — хороший исполнитель, так что практика профилактической настройки запросов SQL Server до того, как они были выполнены хотя бы раз, может наделать больше вреда, чем пользы. Затем мы обсудили основы создания наших ВД только для чтения. Легкая часть этой задачи — моделировать то, как мы хотим видеть данные, но другие задачи, типа задания тиражирования, использование сервисов преобразования данных и т, д. — намного болй сложные и вовлекают для решения других сотрудников тина администратора ВД, В обсуждение была включена идея относительно совдания модели предприятия, объединяющей все наши ресурсы ВД. Наиболее важная причина заниматься этим состоит в том, чтобы гарантировать, что мы храним любую часть данных только однажды в БД. Наконец, мы тщательно рассмотрели, как организовать нашу среда разработки, используя итерационный процесс с тремя стадиями, включающий разработку, проверну характеристик и реализацию. Такой цикл требует рассмотрения проблем типа аппаратных средств, размера данных и характера тестирования того, что мы должны реализовать. Мы также рассмотрели потребность разработки планов обслуживания и восстановления поломок. Рассмотрев все эти проблемы, мы должны затем послать ВД пользователям и позволить им начать использование нашего создания, в то время как сами отправляемся после завершения к следующей большой проблеме создания новой ВД.
12 правил Кодда для СУРБД В то время как большинство из нас думает, что любая БД, которая поддерживает SQL. является автоматически реляционной ВД, это не всегда так, по крайней мере, не полностью. В главе 3 мы обсудили основы и принципы реляционной теории, ио никакое обсуждение по этому предмету не будет успешным вез рассмотрения правил, которые вили . сформулироваиы в 1985 г. в статье из двух частей, опубликованной издательством Computer World (Codd, Е, F„ «1в Your DBMS Beatty Relational?» и «Does Your DBMS Run By the Rules?» Computer World, 14 октября и 21 октября 1985 г.). Они также отмечены в http://funa.pepperdine.edu/gsbm/dass/ckenemborough/Codd12R,htmni в 'Fundamentals of Database Systems' (The Benjamin Cummings Publishing Co,, ISBN 0805til7538). Эти правила шире реляционной теории и определяют более точный навор критериев, которые должны быть реализованы в СУРВД, если она должна действительно быть реляционной. Это может походить на устаревшие новости, но данные критерии все еще используются и сегодня, чтобы оценить, насколько реляциониой является БД. Данные правила часто поднимаются при обсуждении, насколько хорошо реализован конкретный сервер ВД. Мы представляем правила наряду в кратким>комментари®ви относительно того, как SQL - Server 2000 _реализует каждое из них. Правило 1: Информационное правило Вся информация в реляционной ВД должвв быть представлена явно ва логическом уровже лишь « Шим — значениями в таблицах. Это правило — в некотором роде неформальное определение реляционной БД и указывает, что каждая часть данных, которые мы постоянно храним в ВД, раеюояовкиш в таблице.
Обычно SOL Server 2000 выполняет это правило» поскольку мы не можем хранить какую-либо информацию в чем-нибудь другом, кроме таблиц. Переменные, используемые в этом коде, не могут использоваться для хранения каких-либо данных, и поэтому они ограничены отдельным пакетом. Правило 2: Правило гарантированного доступа Любая и каждая величина (элементарное значение) в реляционной БД должна быть гараитировавио логически доступна, обращаясь к названию таблицы, значению первичяого каюта и имени столбца. Это правило в основном подчеркивает важность первичных ключей для размещения данных в ВД. Название таблицы задает нужную таблицу, имя столбца задает нужный столбец, а значение первичного ключа определяет строку, содержащую конкретный экземпляр интересующих данных. Другими словами, каждая (элементарная) часть данных доступна с помощью комбинации названия таблицы, значения первичного ключа и имени столбца. Это правило точно определяет, как мы добираемся до данных в SQL Server. В таблице мы можем искать данные по значению первичного ключа (который гарантированно является уникальным на основе реляционной теории), -и как только мы найдем ^отроку, к данным кожно обратиться с помешаю имени столбца. ' _ ' '' Правило 3: Систематическая обработка значений NULL Значения NULL (отличающиеся от пустой строки символов или строки пробелов и отличные от нуля) поддерживаются в СУРБД для представления систематическим способом отсутствующей информации, независимо от тий* даиных. Это правило требует, чтобы СТРБД поддерживала различные представления NULL независимо от типа данных. Значения NULL отличаются от пустой строки символов или любого другого числа и интерпретируются согласно настройке ANS1_NULL. Когда задана настройка ANSI_NULL, с NULL обращаются естественным образом, так что NULL ♦ 'Значение строки' NULL. Если настройка ANSI_NULL выключена, это позволяет осуществить совместимость с ранними версиями SQL Server; значения NULL представляются нестандартным способом, так что NOLL- + 'Значение строки' « 'Значение строки'. Правило 4: Динамический оперативный каталог, основанный на реляционной модели Описание БД представлено на логическом уровне таким же образом, как обычные данные, так что зарегиетрироваяжы* пользователи могут применять тот же самьш реляционный язык для запросов к пей, который они используют для обращения с регулярными данными. Это правило требует, чтобы реляционная. ВД была самоописываемой. Другими словами, ВД должна содержать некоторые таблицы системы, чьи столбцы непосредственно описывают структуру БД, или другими словами, описание ВД содержится в доступных для пользователей таблицах.
Это правило все больше даадизуетея с каждой новой версией ЭДЬ-Зш^ег^-Используя системные представления ItJF0RMATIOW_SCHEMA (информационная схема). Однако это не совсем точна, потому что wra* представлеяив системного каталога, не имеет . - - нормализованную форму; если вы это было так, то мы могли бы осуществить доступ к данным таким же образом, как к таблицам пользователя. Следовательно, мы должны найти информацию а схеме БД, иепвльауя системные функции SQL Server, чтобы получить дополнительную информацию иа каталога. Правило 5: Правило общего языка манипулирования данными (ЯМД) Реляционная система может поддерживать несколько языков и различные методы терминального использования (например, способ заполнения бланка). Однако должен существовать, по крайней мере, один язык, операторы которого могут быть выражены с использованием некоторого четкого синтаксиса в виде строк символов и который может явно поддерживать следующие операции: определение данных, определеиие представлений, обработка данных (диалоговая и программным образом), ограничения, связанные с целостностью данных, и задание границ транзакций (начало, фиксация и откат). Это правило разрешает использование реляционного языка ВД, типа SQL, хотя SQL требуется не обязательно. Язык должен выть способен поддерживать все основные функции СУРБД — создание БД, извлечение и ввод данных, обеспечение безопасности ВД и т. д. Язык Transact-SQL выполняет эту функцию для SQL Server я выполняет ясе определения данных и задачи управления, требуемые для доступа к данным. Правило 6: Правило корректировки представления Все представления, которые являются теоретически корректируемыми, также корректируемы и систеяой. Это правило относится к представлениям, которые являются виртуальными таблицами, используемыми, чтобы дать различный пользователям ВД различные представления ее структуры. Это — одао из наиболее перспективных правил для практической реализации, и сегодня никакой коммерческий продукт не удовлетворяет его полностью.' Представление теоретически корректируемо, когда оно составлено из столбцов, которые непосредственно соответствуют- реальным полям таблиц. В SQL Server представления являются корректируемыми, если вы корректируете ве более одной таблицы в операторе; при этом вы не можете модернивировать-нмчисляемое или постоянное поле. SQL Server 2000 также реализует триггеры INSTSAD QF, которые вы можете . использовать для представления (глава 11). Поэтому это правило фактически :- выполняется, используя триггеры INSTEAD OF, хотя это ж является косвенным способом. 23 13« 631
Правило 7: Операции добавления, корректировки и удаления высокого уровня Способность обработки основных или производных отношений в виде единственного операнда при меняется те только к получению данных, но также и к добавлению, корректировке и удалению тайных. Это правило подчеркивает ориентируемый на наборы характер реляционной ВД, Оно требует, чтобы со строками обращались как е наборами в операциях добавления, удаления и корректировки. Правило предназначено для того, чтобы запретить реализации, которые поддерживают только тавигадаонщгю-тюдафикшдао ВД, связанную с конкретной строкой. Оно реализуется в языке SQL с помощью операторов INSERT, UPDATE и DELETE. Правило 8: Физическая независимость данных Прикладные программы и терминальные операции остаются логически незатронутыми всякий раз, когда производятся любые измевеижя в размещении данных иди методах доступа. Приложения должны продолжать работать, используя тот же самый синтаксис, даже когда сделаны изменения в способе, которым ВД внутренне реализует хранение данных, и в методах доступа. Это правило более жесткое для реализации, чем другие правила, и подразумевает, что способ, которым данные физически размещены, доджей быть независим от логического способа, которым к ним обращаются. Это означает, что пользователи не должны беспокоиться относительно того, как данные размещены или как к ним обращаются. Например: □ Добавление индексов — индексы определяют, как данные фактически размещены* однако пользователь никогда не узнает через SQL, что индексы используются. О Модификации механизма размещения — время от времени фирма Microsoft будет, очевидно, должна изменять работу SQL Server. Однако операторы SQL должны иметь доступ к данным точно так же, как они делали в любой предыдущей версии. Microsoft затратила много усилий в этой области, так как обладает собственным , реляционным механизмом и механизмом размещения, a OLE DB используется, чтобы передать данные между ними. Дальнейшее чтение иа эту тему возможно в SQL Server 2000 Books Online, раздел Database Engine Components (компоненты механизма ВД). Правило 9: Логическая независимость данных Прикладные программы ж терминальные операции остаются логически незатронутым и, когда над основными таблицами произведены любые изменения в хранении информации, теоретически ие вызывающие ухудшение. Наряду с правилом 8, это правило изолирует пользователя или прикладную программу от реализации низкого уровня в БД. Вместе они определяют, что конкретный доступ или методы размещения, используемые в СУРБД, даже при изменении структуры таблиц в ВД, не должны затронуть способность пользователи работать с данными. Это правило весьма
трудно реализовать, так как цель состоит в том, чтобы оградить структуры таблиц от фактических пользователей таблицы. Таким образом, если я добавлю столбец к таблице, или если таблицы разбиты так, что не добавляются и не удаляются столбцы,, прикладные программы, которые вызывают БД, должны быть незатронутыми. Например, пусть мы имеем следующую таблицу: Здесь baseTable — таблица «основная таблица#; basbTablelD — идентификатор основной таблицы; col ижлД — столбец 1; са1итп2 >— столбец Я. ...и вертикально разобьем ее на две таблицы: ...а теперь создадим следующее представление: CREATE VIEW haselable AS SEbBCT baaeTabielD, со1швп1* coliaanZ В FROM baseTableA JOIN baseTableB - | ON baseTafelaA.baeeTabXelD = baseTableB.baseTablelB ... пользователь должен быть незатронут. Вели мы должны реализовать триггерыIKSTBAD OF для представления, которое имеет то же самое число столбцов е теми же самыми именами, то можем встретиться с этой потребностью. Обратите внимание, что обработка столбцов идентичности может быть ж представлениях довольно хитрая, поскольку они требуют, чтобы даяние выли введены, даже когда они не будут использоваться. Детали можно посмотреть в главе 11. В главе 12 мы рассмотрели использование распределенных разделенных представлений, чтобы горизонтально разбить таблицы на тождественно структурированные таблицы. К атим новым таблицам мы затем обращаемся, используя представление, которое снова объединяет таблицы вместе, позволяя таким образом обращаться к ним, как к единой таблице. Это фактически выполняет критерии правила 9, Одно замечание: мн можем выполнять требования этого правила, реализуя промежуточный слой между пользователем и данными, используя хранимые процедуры; хранимые процедуры тогда становятся «пользователями» данных.
Правило 10: Независимость целостности данных Ограниченна, связанные е целостностью данных, характерные для конкретной реляционной БД, должны быть определяемы на языке реляционных данных и размещаемы в каталоге, а не в прнкладаых программах. Должны быть минимум два следующих ограничения, связанных с целостностью данных: □ Целостность сущности: никакой компонент первичного ключа не должен иметь значение NULL, О Ссылочная целостность; для каждого значения внешнего ключа, отличного от NULL, в реляционной БД должно существовать соответствующее значение первичного ключа того же самого домена. Это правило означает, что язык БД должен поддерживать ограничения, связанные с целостностью, сортирующие данные, которые могут выть введены в БД, и корректировки БД, которые могут выть сделаны. Другими словами, СУРВД должна поддерживать определение и выполнение целостности сущности (первичных ключей) и ссылочной целостности (внешних ключей). Это правило тревует, чтобы ВД была енособна обеспечить ограничения для защиты данных от недопустимых значений, и что необходим аккуратный проект ВД, чтобы гарантировать ссылочную целостность. SQL Server 2000 выполняет большую работу, обладая средствами, чтобы сделать это правило действительностью. Мы можем защитить ваши данные от недопустимых значений еще больше, используя различные ограничения и триггеры. Большая часть главы 11 посвящена рассмотрению методов, которые мы можем использовать в SQL Server, чтобы реализовать независимость целостности. Правило 11: Независимость распределения СУРВД обладает независимостью распределения. Независимость распределения подразумевает, что пользователям не придется знать, распределена лм ВД. Это правило говорит, что язык ВД должен быть способен управлять данными, расположенными на других компьютерных системах. В сущности, мы должны выть способны разбить данные СУРВД на несколько физических систем бе» участия пользователя. Это становится действительностью, начиная е SQL Server 2000. С SQL Server 2000 понятие объединенных серверов ВД с полным разделением нагрузки становится реальностью. Дополнительную информацию на эту тему можно найти в SQL Server 2000 Books Online, раздел Federated SQL Server 2000 Servers (объединенные серверы SQL Server 2000). Правило 12: Правило единственности Если СУРВД имеет язык низкого уровня (единственная запись одновременно), то этот явык низкого уровня не может использоваться, чтобы нарушить или обойти правила целостности или ограничения, выражениые на реляционном языке более высокого уровня (иееколыго записей одновременно).
Это правило в основном требует, чтобы дополнительные методы доступа к данным не могли обойти ограничения, связанные с целостноетыо, означающие, что пользователи ие смогут нарушить правила ВД никаким способом, Для большинства приложений SQL Server 2000 это правило выполняется, так как там нет никаких методов получения данных е-трок и изменения значений, отличных от методов, предписанных БД, Однако имеются два места, где в SQL Server 2000 это правило нарушается: □ Массовые копии — по умолчанию программы массового копирования могут использоваться, чтобы вставить данные прямо в таблицу, минуя проверки сервера БД. □ Запрещение ограничений и триггеров — имеется синтаксис запрещения ограничений и триггеров, таким образом, нарушая это правило. Важно удостовериться, что вы используете эти две особенности внимательно. Они оставляют зияющие бреши в целостности ваших данных, так как позволяют вставить любые значения в любое поле. Так как вы ожидаете, что данные защищены ограничением, которое вы применили, в значениях данных могут возникнуть ошибки в программах, которые используют данные без их повторной проверки. Заключение 12 правил Кодда для реляционных БД могут использоваться, чтобы объяснить многое относительно того, как SQL Server работает сегодня. Эти правила были главным шагом вперед в определении, может ли продавец ЦД называть свою систему ^реляционной», и предъявили жесткие условия реализации для разработчиков ВД. Через пятнадцать лет даже реализация наиболее сложных из этих правил становится достижимой, хотя SQL Server (и другие СУРБД) все еще не достигает всех их целей,
Предметный указатель Основные принципы предметного указателя Этот предметный указатель охватывает все пронумероваииме главы и приложение. Ои организован в алфавитном порядке, пословно, с лимипляхгв в цифрами, прелитептвуютцими букве А. (Вначале перечисляются английские слова и словосочетания, а затем русские. Прим, перев.) _ Там, где основная тема имеет ссылки на страницы и подтемы, ссылки на страницы включают основные описания темы, в то время как подтемы указывают страницы, где упомянуты голые® частные аспекты. Предпочтение отдано аббревиатура?-! по сравнению с их расшифровкой, ва том основании, что незнакомые аббревиатуры легче построить, чем их расшифровать. Символы # префикс, локальные временные таблицы, 267,454 ## префикс, глобальные временные таблицы, 454 $ (знак доллара! разделитель, 321 @® err о г переменная доступ к номерам ошибок в SQL, 384 Qigrowcount переменная запрос посте выражений SET, 384 giStrancount переменная, 440 хранимые процедуры демвкиы оставлять неизменными, 495 Олате и другие параметры стандартных - редел ур расширенным сзсйств, 331 Эрагтппате = синтаксис функции, определяемые пспмсезтелем, 3S2 [] (квадратные сковки} заключение имен объектов SQL Sewer, 267 заю’-очение имени таблицы с мрсэвряюомниым словом,ЗЗв ' ‘ (двойные кавычм) заключение имен объектов SQL Server, №Т <условия_поис«а> значение ограничения-проверки, 376 1НФ (Первая нормальная форма}, 152 одинаково» число значений во как экземплярах сущности, 1S5 признав», что сущвстцлещие данные не - удовлетворяют 1НФ, 158 примеры нарушений, 153 теп данных •за’вРтефакти,чески является нарушением, 158 требование, чтобы все записи выпи различны, 155 учебный пример, 172 2НФ (Вторая нормальная форма), 159 определение отношений и нарушений, 161 признаки, что существующие данные не удозпегаоряют Э<®, 162 применяется к составным ключам, 159 ЗНФ (Третья нормальная форма), 163 необходимость дальнейшей нормализации, 181 гр Юна >:и. что существующие данные не удозле^зеряют ЗДФ, 167 4G Т (4 гигабайтовая на стро й ка), 564 4НФ (Четвертая нормальная форма), 182 троичные отношения, 182 учебный пример, 187 5НФ (Пятая нормальная форма), 186 А АС® (Atomicity, Consistency, Isolation and Durability), свойства, 4S6 блокировки вставляют транзакции работоспособными 443 ActiveX (группа технологий программирования на основ* модели СОМ) технология доступа к данным см АДО
ADO (ActiveX Data Objects) Аст-курсоры, 459 могут посылать транзалр*) в несколько пакетов, 453 Ah I ЬК триггеры, 400 AFTER триггеры обновления, 311 несколько триггеров AFTER, 401 - - определение. 360 организация принудительных отношений, 404 проверка диапазона, 405 проверка распределения сделки, 434 воответстаие почтовых индексер и городов, 432 ALTER TABLE, оператор добавление ограничений, 319 определение. 320 ANSI-NULLS, опция, 471 ANSI.PADDINQ опция, 283 АН (AppBceBsn Programming interface). 459 АР! курсоры, 455 Application Memory Tuning см. настройка памяти приложения ASP (Active Sarver Page), 514 ATM (Autaeiattc Teller Machine) см. банкомат. Available Bytaa, Счетчик, 563 AWE (Address Windowing Extensions) 565 В Back Office Resource Kit срадетаа настроят харакгеристж 567 BCF (Bulk Copy Program), 4« BEGIN DISTRIBUTED TRANSACTION. оператор, 533 BEGIN TRANSACTION оператор, 438 _____ BETWEEN, оператор в операторе WHERE, 279 BLOB (Binary Large Objects). 283 размещение в SQL Server, 283 TafirMja домвноа, учебный пример, 145 Books Опйпв (электронный сп равочник), 45, 267 PERMISSIONS, функция 627 SQL-DMO, 248 бпавфовт, 444 выполнение -раязакций в ADO, 453 детерминированные и наддтадминированные функции 3'0 компоненты механизме ВД. 632 объединенные серверы SQL Server 2000,634 определение значений NULL, 46В определение размеров таблиц и индексов 550 распределенные запросы и тиражирование. 533 расширенные свойства, 33S системные храни voie процедуры. 330 создание индекса, 308 создание индексируемые прз^стгепений. 480 тиражирование и* других ВД, 245 Buffer Manager: счетчик, 572 □ деревья использование для расчета области индекса, 553 использование реагизомжялс индексов 308 С CAL (Client Access Licences) отличие от есадинания пользователей, 584 CASE, ключевое слово использование для воаможных значений NULL, 473 CEILING, функция. 552 CHECK, опция офаничвиие-проварка, 375 CHECK-CONSTRAINTS, Слисок информационная схема 328 Chen ERD, методология, 133 COALESCE, функция ' использование для возможных значений NULL, 473 орозерки-ограничения, 377 COLLATE, оператор, ЗИ COLUMN_DOMAIN_USAGE, список информационная схема, Э2Я COLUMN-PRIVILEGES, список информационная схема, 323 COLUSBIS, список инфорыацио»«ая схеиа, 327 опврагар INSERT, 38S columns_updated(), функция, 381 COM (Component Object Modal) COM- завершение a двумя стадиями, 532 реалиаш® СОМ-обьестов, 248 COMMIT TRANSACTION оператор определение, 440 CONSTRAINT_COLUMN_USAGE, список информационная схема, 32? CONSTRAINTTABi.FlJSAGF. список информационная схема, 328 CREATE FUNCTION, олеретор. Звв CREATE TABLE, оператор, 266 испагшзомнпе для определения столбце в, 270 CREATE TRIGGER, оператор, 387 CREATE VIEW, оператор 472,475,477 CROSS JOIN, оператор пример оперг-ора «Проиаведение», 36 CRUD (Create, Read, Update, Delete), процедуры, 533 CURRENT OF, ключевое слово, 462 О Datalog/Monitor, серине, 5Б6, Ш DBCC (Database Consistency Checker) см. модуль контроля непротиворечиво ста ВД UBCC MEMUSAGE, 566. 567 0 ВС С OPEN TRAN определение длите ль ной работы трвязакций, 584 dbo (data ba se owner) см. владелец вд. DDL (О ata De fi r.i i I on LenguBge) sm. ЯСД. DEADLOCK-PRIORITY свойство, 451 DELETE, оператор триггеры INSTEAD OF могут потребовать повторения, 409 хранима» процедура альтернатива. 511 DELE I си, таблица временная таблица, испопыуемая триггерами, 388 DENY, команда, 525 DISKPERF -Y, команда, 566 DISTINCT, ключевсе слоао пример оператора «Навар», 55 DML (Data Manipulation Language) си. ЯМД. DMO (Data Management Objects), 248 код для реализации и связи, 24В атавржёнив ролей безопасности БД, 526 DOMAIN-CONSTRAINTS, список информационная схема 320 DOMAINS, список информационная схеме, 329
DROP, команды вместе co свазьванием схемы, 366 DTC (Distributed Transaction Controller}, 246 см. коитроплвр распределенных транзакций DT3 (Data Transformation Services) см. сервисы преобразования данным DW (Data Warehouse) см. хранилище данных Е email адреса нарушают 1НФ, 168 возможности почты SQL. 248 English Query, 614 Enterprise Manager задание определяемая пользователем рдачиив, 5И окно запроса хак пример «тонкого» клиента, 250 создание ролей безопасности, 524 средства првдстаалвн»«диафШ4м, 134-135 ERwin, средство моделирования представление аторкмыхввочга, 114 ЕХЕС, оператор определяемые пользователем функции, 361 F FLOOR, функция, 552 fnjistextendedpreperty, системная фунгиия. 331, 335 FTP (FHe Transfer Protocol) CU- протокол передачи файлов G GO, оператор пжвтироввние- транзакций 443 ’ GOTO, операторы 517 GRANT, команда, 525 GROUP BY, оператор использование кластерных индексов. 310 столбец типа bit, испопьаупоивйтйи данных tinyint, 274 GUID (Globally Unique Identifiers) ем. г повальный уникальный идентификатор I IDEF1X (Integration Definition for Information Modeling}, методология, 105 домены и nsследование пизов данных, 115 обозначение отношен*#, 130 определение, 104 представление отношений, 118 представление сущностей, 110 IE (Information Enginc-enncl), 132 IEEE стандарт ДЛЯ величин с плавающей гонкой, 277 IGNORE_DUP_KE¥, опция, 314 Information Engineering, методология необязательные отношения, 132 представление отношений. 132 Iniormati □ п Schema (инфо рмациои пая сима) определение, 52 предпочтительней системныххэг-ииых процедур 328 представление parameters,.462 сообщения о структурах таблиц, 325 INSERT, операторы не следует кодировать Saa-сатя аапаА, 300 список столбце*, используемых триггером, 388 триггеры INSTEAD СЖ могут потребовать повторения, 409 . .. хранимая процедура, альтернатива, 506 INSERTED, таблица временнзя таблица используемай Грегерами, 388 пример использования триггера INSTEAD OF, 407 INSTEAD OF, триггер, 407 игнорирует каскадированные модификации 411 исоогозоагние для мгздифияациц врадстаапяний 416,631 спредепение 386 проблемы, 378 сгюжно 1ф^внтроп^юдать1да*мцы для несколымх ствол 410 цоздачиа таблицы .скрючен ий. 413 удаление времени из величин типе drtetinw, 431 IP (internet Protocol}, адрес 1 НФ и целые числа без зинм. 154 размещение в SQL Sarver трзбуе- тип bigW, 273 В KILL, оператор, 471 ^MEMBER,функция, 526 ISBN (International Standard Book Number) «в ISNULL,функция испальзованиедая всяаоийвж значений NL&L. 473 J JOIN, оператор, 56 К КЕY_COLЦМNUвАВВ, список информационнее схема 328 L LME, маска провврка-пграничвнив-да основе функции. 380 LOCK-TIMEOUT саойстао, «1 м Maybe (сравнения с NULL) логический эквивалент Urttnown, 469 MDS {Meta Data Services), 325 MDX (Multidsniensional Expressions) дост^! к ей гринам данных жаавйвгтот MDX 15 memory см. память. М S_Des с ri pt io п. таблица, 332 N newW( I, функция, 290 NO ACTION, опция каскадирование удалений, 323 «яа
NOCHECK, опция rpoBSpra-O:раничвние, 375, 378, 382 NOCOUNT, опция. 294 North wind. пример ВД :»pns«Ma-aj«. сродства пострсвния диаграмм, 134 NOT IN, оператор оператор 'Разность', 58 NULL-ограничения иатользуемыв прззипа догов» вы» на демив БД, 418 ив является фвктичесяши ограничениями 371 определение 45 NULL, значения, 4ВВ использование значений NULL а погичеееих доменах, 346 использование ключевого слова CASE. 473 использованне типов денных, определенных пользователем, противоречию, 279 используется для необязательной «личины, 52 макет вызван» провпвиы с ва риач-гными данными, 267 определение в Books Online, 468 определение столбцов, допускающих значение NULL, 296 сшибки. свяатыа с йогользоаакием, 471 преобразование запрс-са илл-;стрируощее проблемы, 491 сравнения.4в9 ссылки внешних ключей таблиц-потомков, 407 треть» правило Кодда для СУРВД, 630' уменьшение с 1Юмощыо нормализации, 150 NUMER!C_ROUND ABORT, опция, 275 n-уровневая конфигурация, 254 проблемы с засчитай данных. 417 пул соединений. 243, 254 О ODBC (Open Database Connectivity) API-ку ссоры 4-59 источники данных и распределенные запросы, 532 OOS (Оре rational Data Store') ем, оперативное хранилище данных. OLAP (Online Analytical Programming) данные должны включать индаясы, 546,549 как технология форшфомиия запросов в витрине данных. 20 рост таблиц и факторы роста, 545-648 характерис-ики БД, 546 OLE DB АРйсдесорн 456 DTS и DTC требуют подходящие источники, 246 источники данных и распредепенныо запросы, 532 OLTP (Online Transaction Processing) банк в качества иллюстрации, 11 нормализация структуры 11 оптимизированная для ускорения транзакций, а на для чтения, 9,12 пос-блемы, связанные с ODS, 12 про-цаоположность хранилищу данных, В старости роста таблиц, 547 характеристики БД, 546 Operational Date Stores см. оперативное хранилище данных. OS (Operating System) см. ОС Р РАЕ iPtjsical Address Exte ns loo) 565 Pages Fauits'sec, счетчик, BBS PARAMETERS, список информационная схема, 327 PDF (Package Definition File), 355 P erf Mor (Wi ndow NT), 566 превври блокиpcаки с помощью опсизделиомых пользователем счетчиков, 587 PERMISSIONS, функция, 526 PhysicalDisk: счетчики, 582 Processor:% счетчики контроль работы ЦП, 57S Q Query Analyzer выполнение динамической строки индексов, 555 роль в настройке фу нкцисн проза и ия, 597 сообщения об ошибках 383 QUOTEDJDENTIF1ER, опция, 267 R RAI D (Redundant Array of Inexpensive Disks), 578 RAID 6,57» RAID 1, m RAID 5 как г одой стена выборе. 580 RAID 10.581 Преимуществе нескольких: Систем RAID 5, J-G2 RAISERROR, оператор выполнимый после выражений SET, 384 хранимые процедуры. 48В RAM с*' пенять. RDBMS (Relational Database Management Systems) см. СУРБД RECOMPILE, опция хранимые процедуры, 504, 521 REFERENTiAL_CONSTRAiNTS, список информационная схема. 328 Kt TURNS, оператор тсебсваниявОПФ, 360 REVOKE,команда,525 RFP (Request For Proposal; см sanpoe предложений RFQ (Request For Quote) см. запрос о цене ROLLBACK TRANSACTION оператор, 440 ROUTINE-COLUMNS, «жиж информационная схема, 327 ROUTINES, список информационная схема. 327 гтчрйФ»!, свойство, 290,317 S SAVE TRANSACTION оператор, 441 SCHEMATA, список информационная tawwa, 326 SELECT, операторы хранимая лрецедура, 5С0 определяемые пользователем функции 361
SET, команда для курсоров, 460 S-MP i'Sytnmetrc Mufti-Processorj конфигурация, S74 spJndexjgetKeyData, хранимая процедура ас помог а тельная роль, 554 огфедвпвние, 560 sp tabieScaicDataSpaco, хранимая процедура, 550,551 spJableSindexSpace, хранимая процедура, 554 определение,550 учебный пример, S89 s р _add exten d ed property, хранимая процедура, 331 sp_adduser. хранимая процедура, №Л sp_calumn^privileges. хранимая процедура, 328 «р_секинпс, хранимая процедура, ПТ sp_configure, хранимая процедура, ЗМ настрвйка распределения памяти сервера. 568 ограничение числа соединений гюльзсъзтелей. 583 sp databases, «ранимгя процедура, 326 sp_datatypes_info, хранимая процедура, Mt sp_dboptlon stored, процедура, 297, ЗМ задание БД только /я» чтения. 598 spjkeys, хранимая процедура, 328 •pjwlp store d, процедура, 299 sp_he!pconstraint, хранимая процедура, 318.328 sp_heiptext, хранимая процедура, 327 sp-pkeys, хранимая процедура, 328 sp_server_info, хранимая процедура, 326 sp_speclal_columns. хранимяя процедура, 328 sp_sproc_coiumns, хранимая процедура. 327 sp_statlsties, хранимая процедура, 328 sp_s’.ored_procedu res, хран иная процеду ра, 327 sp_>able_prtvileges, хранимая процедура, 329 spjableoption. хранимая процедура, 284 spjables, хранимая процедура. 326 sp_updateextendedproperty, хранимая процедура. 331 sp_user_counterX х, хранимая процедура, Ив SQL (Structured Query Language) см. также T-SQL. T-SQL — версия Microsoft SQL. 17 все интерфейсы могут выть построены ив хранимых процвдарах, 493 необходимой!» а рхитектору данных падде ржи вать навьим кодирования. 438 операторы в реляционной алгебре. 54 операторы обновления данных подчиняются седьмому правилу Кодда, 632 сравнение компилируемого и нерегламендфованного. 51g ребует элементарные поля 155 триггеры, операторы SQL, 387 SQL Profiler ей. SQL Server Profiler. SQL Server оцени значения NULL 470 • реляционная модель ВД. ЗВ средства построения диаграмм для версии 7.0,134 SQL Server 20С0 динами^юржвя настройка гяшгт, 569 источник йнфэрмтрм о SQt. Server Д00,261 соотввтствиа правили» Кодда для СУРБД, 629 SQL Server 2060, новые возможности гиб гае задан ие сешми'ШЛМнш, 305 индексированные и рятдаленныя представления 479 индексированные л сед ста в,пения, Inpexec views, 238 каскадирование обновлений а объявленной ссылочной целостности, 33 масоое а я загрузка транзакции. 558 ОПФ (определяемы» пользой телем функции), 358 полностью подперииеает вычисляемые столбцы 2S8 разив щемив вариантных данный, 2S5 расширенные свойства описательных объектов, 330 еввзьидаив схемы SQL Server Agent ийюлыованиедпя планирования работы, 24S SQLServer Books On'ine см. Books Online. SQL Server Profiler, S66 используется для проверки перепроектируемых приложений, 166 как средство формирования сценария, 332 роль в настройке фунндееиировзния. 597 SQL-DMO см. DMO. SQL-код компилируемый, 460,492 ем. таим процедуры хранимые; т ри г ге р ы. сравнение в ивретаммтромнными запросами, 478. «О SQL-почта, 238 SSN (Social Security Humberi пример типа данных, определяемого пользователем. ИЗ substring! I, функция, 156 sysmessages, таблица,486 Sy sMon (Windows 2000). S№ проверка блокировку с поиидаватрядапявмых. пользователем счетчшвв, 587 Syst»m:% Total Processor Time, счетчик контроль работы LJ1 575 т TABLE_CONSTRAINTS, список информационная схема, 328 TAB L Е_Р RI VILE SE S, список информационная схема, 32в TABLES, список информ а цис-нная схема, 32в timestamp, столбцы исгвпьювамж хранимой прдцодурь! обиовяемвь 510 иепопыованив для оптимистической блокирюеки 301 использование типа данных datetime, 280 тип данных timestamp, 288 trancount переменная, ЫТ хранимы* процедуры ие допаны изменять, 495 truncate tog on checkpoint, режим 558 TRUNCATE TABLE, оператор. 401 T-SQL (Transact-SQLi c«. лак« t SQL, вторичные таблицы, 456 использование курсоров час» медленнее, чем операции с набд-ром данных, 460 общий язык манипупи^даниядаявли», в#1 определение, в список команд, на допустимых в триггерах, 385 и OOF (User Defined Functions), 360 см ОПФ. УОТ (User defined Data Types) си. типы денных, определяемые пользовгтепем U ML (Unified Mode I ing Language 1 IM объеK-HO-,3□ иентироваНН=|й предок моделей Um С«», 104 ЛЛ1
Unicode, набор ыинаопоа допустимые символы в именах вез огрвврвнвлей, 2в7 изменение сопоставления для сортиров»!, 305 типы данных nchar, nvarew и iiiaxL 28В ИМШ оператор, ST здздврнативв использованию ромоиньж таблиц, 458 црчет ровнее тестирование с помощь» триггеров, 3»3 распределенные секционированные представления, «0 Unknown "Maybe-* ни лгоичаси,-й эквивалент, 469 UPDATE, оператор выряквнив ВДТ, исгюлыувмов трипврвми, 380 обновление «урсоров, *81 зданимая процедура, а л гое р в ата ца. 508 URL iUniform Resource Locator), 75 Use Case модели 105 включение информации о бваездености, 211 клавы отношений, 107 описательные таблицы для Use Case, 1M определение, 103 специздьные хранимые процедуры, 538, 540 учвбньй геймер 138, 228 USER, функция. 526 V VBS (Visual Basic Script), 514 VIEWS, список информационная схема, 326 Vi»to, методология моделирования, 105,134 Visual Basic логические тапы и ислопиовашш- тапа санных smalfint 274 W WAN (Wide Awe Networks) at. теЬмМй сеть. ‘Л'вб-приложеиия БД толь» дая чтения, 813 юпии данных только для чтения 244,25в решения, ориентированные на БД только для чтения, 606 скорость соединения и доступ к данным. 240 WHERE, оператор иелальаоваииа всех столбцов дай оптимистической ВлояфввпцМ! оператор BETWEEN, 280 пример оператора «Ограничение», 55 Windows М0в ОИа Center, ОС, 585 Windows Authentication, 523 Working Set: sq I serve г, счетчик, 872 z Zip коды. 200 проверка соответствия городам, 432 А АБД- администратор базы данных, 559 абстрактны* сущности, 69 администратор БД выбор аппаратных средств, 545, 622 настрой» фунщиониромния, 5Э6 отличив от проектировщика БД, 7.22 адреса примеры атрибутов указателей на расположение, 74 пртирымрушвнийДНФ, 154,155 требования нормэлизадо, учебный прим ее 174, 176. WS,338 уникальност-г определения разных сущнсс-ей, 178 активная страница сервера см, ASP. алфавитное упорядочивание см, мама сортировка. сопостаияииа в учебной примере 353 типы согюстаипенвд, ЗМ имам, отличные пт английского 305_ американский яочтввый индекс см. ZBA анализатор запроса с.м Query Analyser, аналитическая обработка в реальном масштабе временис.м. OLAP. аппаратные средства ; см. готото дисковая подсистема; память определение требований. 545 память. необходимая для соединения с сервером, 244 потребности в адекватной обеспечении, 243 работ» ждемстямы ЦП, 574 реализация БД топью жя чтения. 607 системы в большим объемом даниьи, 236 требования на стадии проверки харвиристик, 622 требования на стадии разработки. 621 архивирование, 559 доступ к архивным данным, 6ВЭ маркирование зрх.иаиых данньц. 563 не следует запаздывать, 582 разделение-по датам таблицы феетое 56i старание апваратмлс и программных здздств, 560 стоимость аряшнмх даииых, 560 архивирование данных, стоимость. 580 арх ите-ктор далям», рО№>( 22 архитькгурные ограничения требования, наиидыааямыо на ВД, 212 атрибуты 1Н®, должны выть элементарным», 153 2Нв> дотииа описи sale весь ключ, 1® Enterprise Мапауиг, средство ррвдвввлания диаграмм. 134 атрибуты э-рибутдв 43 добавление огвлатепьной ««формации, 136 завершение определений, 350 и домены, олредепение а данных, гюлученных от заказчиков, 66 история чаругоеиие4НФ, 192 ми егозя «чные и 4НФ, 182.189 пбг,< должна яояювшозависеть О' «ивча, 168 отфвдалвяиедоиалнитальньа данных, BS определение стдайюв, 41 готредепения, учв&алй пример, 146 штределяющие сушнод-и, 72 отя«из от столбцов, 74,112 от чоценис китючам и нормализация 158 перемещение при создании. ВД толью для чтения, 602 полученные да обращав документе». 8В, 91, порвдеж ив важен, 42
представление в стандарте IDEE IX 112 пример таблицы, содержащей сущнодта и дамены, 75 проверм нарушений ЗНФ, 165 соглашения ой овв»мнв«иях, 117 учевиый пример, 89 формирование стандарте, испелиуюижр домены IDEF1X, 115 атрибуты неитмчевы» взаимная эааисжюсп» нарушит ЗНФ, '63 атрибуты стандартные создание, испопыуя домены tDEF IX 115 Б балансирование нагрузки обеспечивается кластеризацией сервера, 244 переключатель, для слсхнсго использования Wea-серверов, 255 банкомат, 546 БД (базы данных} 1 см, также БД, котя только для чтения OLTP и OLAP, 546 конечный пользователь и исходные источники данных, 28,596 определение, 38 перевод в 008,816 ' правила определения, действительно пи является реляционной, 62в программирование имеет аналогии с ООП, 41 рекурсивные Tpnrreps<394 реляционная модель, 38 реорганизация для работы с большим объемом данных, 236 рост в связи с вестом витании, 549 средстве генерации ВД. 260 теория, 37 техника доступа и корревпфоеки. 437 БД, копия только для чтения, 244,593 Web-пршюжвнии, иопсльзую д ,-е методы, ориентируемые на решети. НЯ возвращение в режим записи, 698 возможное использование группировки, 613 выбор модели в зависимое™ of пользователя, 614 использование для защиты первичной БД, 598 конфигуращтя одсодод о иимпыомния Web-свдаерав. 255 обоснование, 599 пример корпоративного решения, 812 реализация, 610 учебный пример, 624 частота обновления, fiOS БД на основе OLE-техно л о пи и см. OLE DB, БД, отличающиеся от SQL Sener доступ как «связанным серверам, 246 тиржироаанив, 245 БД, проектирование важность метеленной подхода, М выделение разтмньогропвй, 22 история, В как искусство и наука, 7 перечисление стадий проекта ровен ,гя. 19 различие лопиеского и физического проектирования, 19 разрабепи замедлялась технологией, 8,183 следование стандартам и нормализадаи, 149 следует добавить план архиаиреэь-и'я. 582 сравнение с реализацией, 22g - средства, ввеепвчивдаиые SQL Sewer, 245 стадия сбора информации; 21 схема типичной большой системы, 1в БД. разработка не использовать деловые данные, 260 БД,структура стасываетсг внутри самой ЕД, 930 откаа ст структуры на с -един разработки, вв ВД таблицы см, таблицы. БД, уровень блокировки, 443 ВД формирования отчетов с«. ВД копия только для чтения, безопасности проблемы ем. твкж» доступ к данным. - доступ к данным не другой БД, 530 защита от нежелательного достуги или модификации, 2 V представления, сбывающие основные детали таблиц 474,478 - - роли, создаваемые Enterprise Manager, 524 ситуационный контрол ь доступа к таблице, 485 учевньй датехер, S42 хранимые процедуры, рабодаюод-е на объектном уровне, 494 хранимые процедуры, скрывающие основные детали таблиц, 4Й .. - безопасность SQL Sefyw, 524 безопасность на уровне Страк и нв уровне столбцов, 627 безопасность нз уровне столбцов, 478, 529 безопасность науровне строк, «8, 529 бизнес-прааила ввжнаать-полюо аярвдедвн»я,В1 использование и отюиеквш л г г эед и -а г ам 47 юдароввчив на Функционзлоном жгых», а не на SQL, 255 . -.. лучззаясрганизациянасервере.256 необходимо внимательно формулировать. в2 необязательные ^ав»вш в учебном щэивер», 435 сбеспе чеь/е с гр мощью тригге рон, 366 _ ограничемнда испелвздвян»® триггера INSTEAD OF, 411 определение, 81 определение в структура данных. IBS получение из данных, «эитап at заказчика, 66, 81 >8 реализация через оерв есы среднег о уровня, 417 распрадепеиие мехду сред мим уровне»* и БД, 418 таблида иввых процессов в гримере банка, 99 визнес-прэвила нво&июжпипыс учебный пвнмео, 435 phc-icc решения см. ппдг^рхм решений. бизнвечзтой обеспечение бизмес-правил, 254 . . формирование лупа ссвдавздиий посцедчжа, 254 битовые маски coJuuBS^updatedO воззрадает, 39' битовый массив значений NULL, Mt Ьмкирож* доступ, вызыаающ.'й елрщком длительные бпсЖфовют, 585 доступ, вызывающий усиление блокиреэки. ИВ причины возчиктОаения у эта х мест, 567 проверка сопредгииамымк мяыодатш»»*- счетчиями, SST соединения, повторяемые чтения, coiwectons, 447
блокировка кл юч а или диапазона клю ча, 443 блокировка оптимистическая, 301 использование хранимой процедуры о&ввлвния, 510 учебный пример. 342 блокировка страницы, 443 блокировка строк, 443 блокировка таблицы. 444 блокировка экстента, 444 блокировки массовой модериииции, 444 блокировки модернизации, 444 блокировки направленные направленные совместнее, иапсаг-енвь^ экс»-юзианые и совместные С ча-равле-иными ахсклюзивными, 444 блокировки совместные, 444 блокировки совместные с направленными ЗКСЖЙЛЗИЭМЫЮ!^. 444 блокировки схемы, 444 блокировки эксклюзивные, 444 бумажные носители вимояоюстъ размещения исходных данных, И бюджет =пияи<е выбора характеристик оптимизации., 243 В важная информация маскировка образцы документа в 34 вариант использования ок Use Case модели, ва рианти ые дан н ые см. тип данных »<_wiant величины с плавающей точкой 277 -гезозможн-ость представить некоторые величины ычто, 277 сраан ение с помощью осе эаци и « сомнительно, 278 тип данных Boat, 278 величины с плавающей точкой, мантисса. 278 величины с плавающей точкой, показатель. 271 величины в плавающей точкой, смещение, 278 венгерская нотация, 271 ве рти ка льн ое раздел еиие да иных, 478 безопасность на уровне стопбцсз, альтернатива. 528 верхний ре ги стр см. давись в ст ро ке слов прописными буквами: чувствительность к регистру. витрины данных дсступн ые через средства, не использующие кодирование, 15 отличвюта» от OLAF и кубов, 20 се твиты хранилища данных, 14 владелец БД, 266, 268 владелец БД. роль, 266.268 ob_owner и другие встроенные роли, 525 определяемые по.пьзе-аателвм функции, 362 внешние ключи задание HULL в таблицах-потомках. 407 кесдадиромнные удаления и отношения между БД, 319 определение, 51 отсутствуют для первичных ключей независимых сущностей, 109 первичный ключ мигрируй в отношении "один к п”, 60 подверженность сшибкам в нвнормввводаиных БД, 150 г ре дота зление в стандарте ЮЕР1Х, 114.118 присутствуют в первичных «июнях зависимых сущностей, 110 ссылочная целостность, 634 функциональные имена, используемые для трибутов,124 внешние системы импортирующие прослойки для уменьшения связываики, 216 требования взаимодействия, 213 учебный гример 224 внешние соединения как источники значений NULL, 468,472 возвращаем ые значения хранимые процедуры, протокол, 482 возраст данных период архивирования, 5в1 волокна вместо гютоям используют приложения, не входящие в ядро 576 когда нуимо ИСПСЛьЗОйаТЬ, 576 "волоконное управление*, 57в вопросы предлагаемы» для начального интервью с заказчиком, 27 восприятие заказчика прототип может исказить, 25 восстановление записей большие передачи данных могут быть разбито не чаям, 241 и» списксь, пример хранимойпредадим, 500 восстановление поломок, 622 временная статистика, 597 временные таблицы # кек идентификатор, 267 ШЮ1Ч (АЩ. альтернативный оте ратей, 456 вторичные тебтвд tax альтернатива, 456 замена функциями с несколькими операторами 369 использование отображения рекурсивного дерева. 422 используются менее «вито в SQL Sewer 2000,421 локальные и глобальные различаются префиксами, 458 таблицы OELEIBD и IN S Е RTED 388 лпг.пкткка БД только дда чтения, 598 609 шетвиы принятии решений, 238 формирование отчетов в реальном времени. 475 встроенные функции формирования таблицы выполнение в команде. использующей таблицу. Звв тал ОПФ, 385 вторичные ключи нормальная форма Бейса-Кодда, 171 определение. 49 представление в стандарте IDEF1X, 113 реализация длязбесгукекия уникальности, 317 роль ядиураим у ни кв.льности, 47 уникальность определяется ограничениями, а не индаквми, 313 вторичные таблицы, T-SQL ал ьтернати еа временным таблицам, 456 выборочный откат, 441 выходные параметры хранимые процедуры, 484
Г гл аго л ьные конструкции можно читать в любом направлении,132 обозначение отношений, 130 глобальная сеть практические вопросы редактирования и передач и сообщений, 2S5 увеличение скорости соединения и доступ к данным. 240 глобальные временные таблицы видимы любым соединением или пользователем, 454 глобальный уникальный идентификатор, 115 пример определений домена в B1EF1X, 118 тип данным unicueidemifier, 288 горизонтальное разделение данных, 477 графическое представление см. моделирование даиных; Use Case модели. группировка данных по секундам, 298 д дальнейшие требования документирование, 220 данные, дублирование нормализация позволяет минимизировать, 150 данные избыточные нормализация позволяет минимизировать, 150 данные исходные, 28 см. тмжв наследование данных. определение ошибок с проверкамиюграничениями, 382 перевод в ODS. 816 следует проверить ограничения перед - использованием триггеров, 403 денные, объем см. также объемный анализ. источник проблем формирования отчетов, 235 оцени для аппаратных средств, 545 оценка для столбцов пере май i-ой дойны, 551 последствия удаления записей из таблкд-п р едкое 243 факторы, учитываемою при расчете, 549 дани ыв переменной длины. 282 гдалуюниефактора далотсчяя с помощью ОПФ, 551 данные, подсчет размера иежтыованив хранимых процедур, &Q данные предварительно атрегэтированные проблемы о целостностьс данных, 237 данные, преобразование пример корпоративного решения. 512 данные числовые тип дачных пи тепе, 275 точны» и приближенные типы данных, 272 данные числовые точны», 272 данных зависимости три ог реде.пен ных we, S2 данных защита см, данных целостность. данных потери допустимы» уровнидля гшанса поддержания, 823 данных целостность см. тяж» последовательность. безопасность, четыре метода. 3S7 главная цель OLTP, 11 кэширование даннь« дня проверок мото бы быть компромиссом 419 кэширование записи может выть компромиссом, 57в массово» копирование и запрещение ограничений и Григеров, 635 проблемы n₽»t использовании электрон н six тввпиц, 28 проблем» с предварительным агреттированием данных, 237 проблем:,' саязанные с импортом данных. 418 связь аание схем и защитаданных Звв средний уровень и вда:«еч?е ВД, 418 триггеры, обеспечивающие значения, 418 управление параллелизмом фи блокировке, 300,443 уровни изоляции блокироедк, 448- даты будущие проверка с помощью ОПФ, 428 двоичное дерево, пример, 456 двухфазная фиксация, 248 действие по л ол учению информации о счете специальны» х ра н им ые процедуры, 541 действия по балансированию счета специальная хранимая процедура, 539 декларативные ограничения ключей, 307 декомпозиция тройные о-ношения, 18S декомпозиция с потерями, 186 ''Деление'', оператор, 58 деловой аналитик, роль. 22 денормализации, 196 добавление итоговых дкмых. 266 кодирование в связи с данориапизацией дачных, 163 копии данных топы» для чтения, 266 обработка подтипов может использовать 264 последив» обра ще н ие При настрой ке функционирования. 596. 597 проблемы и*Фвд»н0|ммякюванньрс внешних систем 215 сложные си ту ации формирования отчего» могут оправдать, 237 требуется ODS. 61# хранимы» даацйдары обновления, 610 двтвлизацм типы блокировок, 443 детерминанты се должны быть гдючзии а НФ5К, 170 диаграммы состояния нарушения 4НФ, 192 динамическая память, размещение нарушение заданием соединений пользователей. 583 настройте SO. Server 2000, 569 дисковая подсистема важность знания. 5ВЗ несдельно хонгреллесов диска, 581 настрой» работы, 577 счетчики работы диска, 582 дискриминаторы отношения классификации 127 учебный пример, 336 документация дальнейшие требования, 220 завершение задач формирования документов с данными, 34 маскировка значащей в образ цах, 34 моделирование данньж ж альтернатива неуграв/'яем-ому списку. 97 необходимо постоянно обновлять, 66 окончательный обмр, 218, 229 опредапвни» элементов информации, вв учебный ирдисф, 32 Чрезвычайно важна для проекта БД 23.209,219
fiOKJMSHTb! гак сущности в БД, (Я домены и атрибуты, попучеште и» данных,-попучвнных от заказчиков, €6 ислелыоввние «ж шаблон столбца, 272 использование, объясняемое примерим, 4В логичвсии, преобразование а типы данных, 346 лотичеаиа, учебный пример. 145 названия, таблицы с э«ива.твмныии типами данных, 346 наследование зависит от средств модегировалия 115 определение доменов столбцов, 43 определяется одновременно с-атрибутами, 72 получение домена, подходящего-для атрибута, 73 получение ограничвиийуПрроРШИа -описка ддменоа, 425 представление в IDEF1X, 118-116 пример таблицы с сущностями и атрибутами, 75 реализация триггерами и хранимым ЛВОцедрвми, 46 создание во время «(^мапиидаи, 174,191,1ЯДИ соответствующие типам данных, 272 формирование ие учитывает реализацию, 116 домены данных еж, домены. домены столбцов см, даманы. допустимые значения пвречиспакше методов ограничения, 43 доступ диалоговый см. мобильные клиенты, доступ к архивным данным, 563 доступ к данным си. там» безопасности проблемы. доступ « распределенным данным, 477 задание прав ролям бваопаоисти, 525 защита от нежелательных лсяьзоввтевей, 524 из разных БД, 530 ограничение доступа к првдагимнияя вместо таблиц, 478 ограничение с помощыо хранимых процедур, 520 формирование системе большим капичесгаом денных, 236 через таблицу, столбец и первичный, ключ. 630 доступ к данным между БД, 530 БД на разных серверах, 532 БД на том же сервере 530 доступ к сети реализация SQL .требуется для аффевжности, 18 дублирование {RAID if, 57в Ж живучесть свойства транзащии, 43в 3 завершение важность нормального зааершен/я. 86 пересмотр окончательной документации, 228 проблемы, которых можно избежать, 226 за виси мости с несколькими значениями, 64 4НФ допускает только одну, 183 модель проектирования сущности contact (партнер), 191 загрузка транзакции размеры в OLTP-БД и OLAP-ВД, 556 задание флагов архивированные данные, 563 записи, которые невозможно удалить, 243 запирание, 411 6локироая доступа вызывается расширением блокировке, 238 избежание с помощью првдетаапений 476 оптимизатор запросов выбирает талы, 444 оед-мкстмч&ские и песстшстичвете Ламравш, 301 риск от яснояьзования курсоров T-SQL. 480 споеовы блокировок. 444 типы блокировок в порядке уменьшения данижавции, 443 записи действий как пушноета в БД, ?i> запись в строке спае прописными буквами, 369 см, м«*» чувствительность к регистру. использование триггера INSTEAD OF, 411 определяемая лольаавателем функцюг, 362 запись и*диск оптимизация системы отложенных записей. 577 запрещение ограничеиий и триггеров нарушает двенадцатое правило Кодда для СУРВД. 635 запрос о цене источник правил данные, 29 запрос предложений источник л разил данных, 20 запросы выгодные индексы и реализация, 554 557 лобз вление определяемых пользователем фунщий, 360 максимизация функционирования с БД топы® для чтения, 599 написание в случае значений NULL, 473 ьедтрлйка запросов клиента, 518 реализац*» «явных запос-сов, 608 факторы, влияющие на эффективность, 438 запросы динамические хранимые процедуры 504 запросы неявные, О-Э8 запросы, преобразования пример, иплвстрирующий проблемы, связанные с NULL, 472 зарезервированные ключевые слова, 267, 338 защита данных см данных целостность. зеркальное отображение (RAID! 879, 531 значение <условия^поиска» провески-ограмичения. 378 значения даимы» спредепение для проекте БД, 2fi значения по умолчанию обеспечение а помощью триггера AFTER. 4Q5 значения полей добавление внутренних ограничений по умолчанию, 375 И идентификаторы подходящие атрибуты, 72 идентификационные номера использование типе данных char, 2В4 изменчивые правила кандидаты иа использование хранимой процедуры, 419 примеры, которые кажутся фиксированными, 422
изображения проблемы с рммащвниам, 283 тип данных одаде, 282 изоляция свойства транзакции, 438 иие-наполвй сдданмюпымж ключевыми префиксами признаки, что существующие дадные не удовлетворяют 2НФ, 162 имена полей с одинаковыми префиксами признак, что рущес>ау191ф1едвннью не в ЗНФ, 167 учебный пример, 175 имена полей е чжяитмш суффиксами признак, что существу юшод денные на удовлетворяют 1Н®. 158 имена полные примеры нарушения 1НФ, 153 имена функциональные атрибуты используемые а качестве hhimk ключей, 124 именованны» отношения 40,54 импортирование данных проблемы цапопгнасти, даияых, 418 импортирующие прослойки взаимодействие с итвижимв системами 218 индексы БД только для чтения может иметь множ 607 выгодные и формирование запросов, 554- вычнелвиив размера, исивльзуя епцмимш хранимые процедуры, 554 догтаны вкпючагося в CJLAP-БД, 546,549 используются для организации унисапьирсги, 308 команды DBCC, 587 литеральные значения по умолчвнию, 373 максимальное число, байт на ИИД инде ксэ, 26У максимаданов число «ласте иных и некздетерньм индексов, 2вв "мастер" настройки оддекоз 597 нелинейный рост, 500 нормализация уменьшает потребность, 151 обеспечение уникальных Значений, 313 обозначение необязательно, 31в оставить реализацию да стадии настройки. 319 программный подход к рассмотрению ужа мест, 568 расчет объемов данных, 555 слишком много проблем дат при ф ункционирсязнии, 597 индексы выгодные, 554,557 индексы кластерные, 310 вычисления, определяющие наличие индекса, 556 задание первичным -слюней по умолчанию, 317 нормализация и использование индексов, 151 причине иметь короние индексные точи, 553 556 причина иметь кодатеий размер отрок, 5S6 свободная область из фактора заполнения, 552 учебный пример, 5в0 файеры, определяющие выбор, 310 яадкы уникальные использование с вторичными ключами, 317 обеспечение уникальных значений, 308313 инкапсуляция код внутри ОПФ, 378 кед внутри хранимой процедуры, 481 ,-5И соединения и т, д, для маскировки ст пользователя, 475 типы данных, определяемые пппнзпаатвлям и средства, 282 интегральное определение для информяциенчаго моделирования в». iDEFIX. интеграция одиннадцатое правит» Кодда для СУРБД, 634 интернационализация сложности с размещением номеров телефонов и адресов, 153 интерфейс внешние системы. 213 гибкость, используя нере-гпамв чти рован ний SQL-koa 518 интерфейс на цстнынанном языке ЕодОДл Оиегу, 814 интерфейс пользователя т зависит от изменений хранимой процедуры, 494 интерфейс прикладного программирования см ДИ. информационная разработка ем, IE. информационное правил о первое правила Кодда для СУ Рь Д 829 информация о времени см. танж» информация о дате. размещение и маиигзпвфоиаяив. 280 информация о дате tfaieRart, сисзммий функция 47в использование дат в ншвхне-раимендавтея, 345 период архивирования, S61 проверка, явятся пиддаябудщчцвй, 428 размещение и работе с латой, 280 информация о состоянии нарушения 4НФ. 192 искусственные ключи использование для идентификации строк, 49 испопнитепи, модели Use Cases, 196 включение информации о безопасности. 211 учебный пример, 138 исполняемый код клиента защита целостности данных, 35в и-одол одевание данных, 206 210 учебный пример, 22С используемость уменьшится в случае ««жив» клиеитса, 251 испорченное чтение,585 испытание нагрузки, 621 истинности таблицы сравнения вймвмйкцие NUU,, 460 истерическая информация размещение в хранилище данных, 13 история проектирования ВД 8' источники гетерогенных данных, 246 источники данных сбъеди-ение а БД топы® дая чтения, 5Э& исходные системы иг. лжк» и аследов а н ие данным. одаенлегькодирования. 30 получение бизнес-прайил од старого кода 81 лреобразование двеад. может Вяь неудачньщ, 21? проблемы воднихающие од пстоебностей взаимодействия с ними, 213 итоговые дачные доба s - ен и е в БД топы» дая ч'е н и=, 602,604,666 построение с индексируемыми представлениями, е да триггерами, 4W признак нарушения ЗНФ, 167 причины отхода от погичоддао гфомнь 2W суммирование данных вделок в ODS, 12 учевиыв пример, 175 - : R47
К I, 291
обновляемость. 461 условия испольммиия, 464 “«уча" таблицы без кластерных ^вдексс-б, 311,552 кэширование вызовы 5Д черв умеренные амдинения. 24® контроллером диска, 577 контроль данных может г роти вор еч ить целостности данных, 419 кэширование записи. 577 таны аапрооов, ИО страниц для ускорения доступа к данным. 5П кэширование записи, И компромисс с дапоимостыо данных. 578 Л "легкие" клиенты см мобильные клиенты. литеральные значения по умолчанию, 373 лицензии, важность внимательного изучения, 584 лицензии программного обеспечения, 243 логика распределение в трехуровневой и п-уровневой системах, 254 трехзначная логика, включающая NULL, 468 логическая независимость данных девятое правило Кодда для СУРБД, 632 обеспечивается использование-* представлений. 51 логические домены см. домены. логические чтения параметры работы, 502 логическое проектирование завершающие стадии, 205 использует общие типы данных, 44 причины отхода во «ремжрватяации. 262,266 локальные временные таблице! используются для соединений с SQL Sewer, 454 локальные сети, нарушение работы минимизация влияния пмкпфваанием транзакций 452 локаторы строк, 311 "сучи*. включающие страницу смещение. 312 люди как сущности в ВД, 8? м маски см тесте битовые маски. использование дм проверок данных. 380 массив использование как временные таблицы, 457 массовая загрузка транзакций, 558 массовое копирование нарушает двенадцатое правило кодов дня СУРВД, 635 промежуточные таблицы для уменьшения издержек. 451 масштаб тип данных Bedmaf, 275 масштабируемость ои. теки» пользователи, число. необходимость знать до реализации. ИЗ матрица недорогих устройств с избыточностью см, RAID. менеджер проекта, роль. 22 (1022} места как сущности в ЕД. 68 межтабличные проверки ислслыювание правил додано быть на уровне БД. 418 метаданные процедуры, представления информационной схемы и си<-тсмнь,естандартныепроцедуры 325 элементы отношения ict олбе ц), 52 ggfiSMCgB at. MOS метод водопада, 100 мзуямыш БД различие реляционного механизма механизма размещения 632 миграция первичных ключей идентифицирующие и неидентифицирующие отношения, 119 ик внешних ключей в отношении «один к п», 58 обозначение первичных ключей. 117 м игр ирующие атрибуты, мигрирующие ключи си, внешние ключи. многоуровневый проект см. п-уродпевая конфигурация мобильные НИКИТЫ с,и. таи-е пеоемежа ющи еся едадиишм. БД только для чтвния313-в14 использование ODS дай передвигающегося штата. 13 тиражиревгние спинисы наиболее подходит. 246 удаленный штат может использовать диалоговый доступ, 240 модели данных предприятия, 68 логическая модель првдарчятия^В модели, ориентируе мые на решение БД для формироевниг ртштра, 601 модели, ориентируемые на таблицы ВД дда формирования втчвтсв, 6в1 преимущества здхвв, ИМ модели процессов см. Use Case модели. моделирование данных, 103 см. такт моде л ирование логическое: моделирования методология. альтернатива нвул рааляемому сп иску 97 КдаитифкяфШ соответствующих ripoueccoB. 83 определение моделейланньгх, 1СЗ различие вопмвсюя и физических !иоделей. 10В систе «а счега-фапу р» для преобразования в ВД только для чтения, ИС учебный пример, 140,176 моделирование логическое денормализация нежелательна, 196 должны быть рассмотрено! нормальные формы. 152 итогоаые данные абослютно запрещены. 167 коррекция оимбо^ 3W логическая медвль лргдприятия, 613 мщмшдапюш бьгть живыми догг/ментами 111 незавидимость or реализмом, 196 парпищша итдаш, дайор, 170 ' сущности ж кашитувяьн» версия тайгаш, 41 учебный пример. 173,203 моделирования метздзлстии см. также Enterprise М ападе г; 1РЕ F1X; Information Engineering; Use Сот» модели; мадалировакие данных. тем к медеписюва н и к- Дании. аарсии Enterp rise, 818 o<i ре деление, 104 гэедставпение отношений, 132, АЛЛ
свеспвчиваемwe SQL Swwr, 245 модификация данных высокого уровня' гяднмсо правиле Кодде для СУРБД 6S2 модуль контроля непротиворечивости ВД 484, 597 мощность испопьаувия для обозначения неоднозначного числа строк, 50 отношений, 59 • _ _ гфавила, га* использовать дгвттржтеров AFTER, 405 твбяица шести возможных мощностей, 123 01 н к 235 БД только для чтения, ориентируемые на таблицы, 60S нескол ько каналов данных, 581 О
хранимые процедуры, 485 хранимые процедуры дая проверки возвращаемых значений, 483 хранимые процедуры дая проверки выходных параметров, 483 хранимы» процедуры да» проверки смещений, 491 обработка транзакций s реальном масштабе времени см. OLTP. обслуживание планы обслуживагат, 622 проблемы гастем адяяирования, 580 объединение разделенных представлений, S81 объекты см. СОИ; объекты ВД объекты БД иерархия. ncnonMyenJas- в рвсширеиньи свойствах, 330 как сущности в ВД. 68 средства Форк'кровав»-= отчетов SQL Se<w2000.325 управление премию доступа, 624 объекты управления даннышгем, DMO. объемный анализ см. также данные, объем. определение 545 планирование, 217 стратегия архивирования дшаав быть определена заранее, 55в учебный пример, 225,588 ' Ограничение”, оператор WHERE, оператор, 56 ограничения,371 добавление с ALTER TABLE, 32® завершение сдвпяк, ед подходи- для SQL Server 2000.203 запрещение ограничений нарушаетдвенадцато* правите Кодда, 635 обеспечение целостности дакала, 358 •эпрадвпвнив и классификация 45 определение пяти типов» 37J ошибки от трипедов вшыиют откат, 397 ошибки, содбоджаошие доукестэе-яые сообщения. 488 сравнение с триггерами, 386,3SS ограничения в виде внешних ключей исюпьзуемы* премии дальне. выть на уровне БД, 419 один из пятя типов ограничений 371 ограничения между таблицами и между строками обеспечиваете* триггерами, ЗИ ограничения первичного кимча,371. ограничения по умолчанию, 372 учебный пример, 423 ограничения таблиц тип '1роэерад-о-раничвния 375 ограничения столбцов тип гроедрииотраничеедя, 375 ограничения целостности едлоегедсп> сущности и ссоыочная целое-нодн. 834 ограничители в вид* двойных кавычек, 267 ожидаемая жизнь страницы, 379,872 ОЗУ (оперативно* запоминающее устройство) см. память, округление, 275 ООН (объектно-ориентированное программирование) 41 аналогам оо строками и столбцами таблицы 41 отношение «яиижги» а нале-ин но под классу П оперативное хранилище данных, 815 кпопьзование да передвигающегося штате, 13 объединение данных, зключая определение общих объединенные данные дая ежедневных отчетов, 12 термине, 616 определение OLAP, 546 формат, ключ к праовразввммям хранилищ данных, 16 операторы восемь т.н 'йцис-нных седратс роа ЯцгДЛ 64 модификации и чои -ксвые S8 ’ отличие от кл ючезы к слов SQL 54 элементы рвлярютяой алгебры, 84 операции побитовые двоичные титы данных ив яеддаржиаают, 282 операции е наваром данных альтернативы курсорам, 4 63.463 операции со строками архивирование избранных строк таблицы фак-ов S62 испопьзевание курсоров, 459 460 оперзции только для чтения ограничение опрвдел₽аш« пользователем футаций, 360 операция "иуапечение преобразование и загрузка", 16,617 - описательна,т информация добавление к графически» моделям данные 135 описательны» свойств* вязь с раарвботчикеми, 330 определяемые пользователем счетчики, 586 оптимизатор запросов БД только для чтения соде рж ит индамы ввек столбцов, К8 может использовать седан ичяния, 37$. расширение блокировки ед уссаед строки, 238 уникальные индексы гом стают рабатать;314 кренимые продведрв-легч»-оптимизировать 522 ОПФ (определяемая пользователем функция), 360 встроенные функции формирования тввпиц, 365 доступ к данным столбца переменной длины, 551 задание аладвяща, 362 курсоры и нелокальны» овьевты. 370 нельзя юиючат» некотирые 370 нельзя передавать заданные мпыоватвпем паряметры, 362 описание версий, 360 пример проверни ди аг а зона. 430 провари пустых строк. 376 связывание вхемы, 36с- сравнен» с хранимыми подцедурзми. 481 учебный пример, 424 » - - функции оо многими одер.э торами. формирующими таблицу, 369 ррганивация циклов SELECT для объединяемых tsSchwажиалвнтея цикл.'. 17 ОС (операционная система), 9 ишы источников питания, 623 открытый интерфейсдаот^иажвми» данных см. ODBC отладка - - - ошиб®», ««Инны* с и-noпьзовг»ием NULL, 471 отношение коммуникаций модели Use Сей. ЮТ отношение “имеет” ? - : примфотниавиия ‘один к Х7 78 отношение "испольтует” модели иве Case, ‘07139 отношение "расширяет" модмм Ив* Саве, 107,139 ее»
отношение "является” пример отношения "один к п", 78 отношений деревья создание БД толы» для чтения, 802 отношений список, 79 отношений типы мв.тду таблицами БД, 78 описание менее важных типов, 125 отношения, 40 Enterprise Manage-не отображает триггеры, 135 Enterprise Manager, средство нестроения д«трами, 134 бинарные и ивбинярны*. 58 все су щност и должны выть отношениями и связанными с однсй темой. 2С4 глагольные конструкции дают удобочитаемые предложения, 130 добавление описательной информации, 135 идентифицирующие и неиден'ифицчрующиа 119. 120 методологии, отличные от IDEF1X, 132 методология Ghen ЕШХ 134 между БД требуют триггеры, 324 обеспечение С помощью триггеров AFTER, 403 обозначение 320 однотаыные, 204 определение • дани six. полученных от амеичидю, 66 представление в соответствии с метадол огней моделирования денным, 113 лад похождение реляционной БД, 38 реализация, 319 сущности, 118 таблица примера поставщиков офисного оборудования, 78 таблица различных мощностей, 123 таблице символов используемых a h Голта :юп Engineering, 132 таблица типов в IDEFIX. 118 учебный пример, 69.142 отношения двоичные, 59 отношении идентифицирующие. 11» Information Engineering, методология, 132 графическое представление, 118 нарушения 2НФ, 161 отношения иерархические, 61,125 графическое представление. 118 отношения классификации, 127 полные и неполные категории, 128 отношения между БД, 324 внешние ключи, 3'9 отношения "многие ко мирты’, 62,129 графическое представление, 118 разрешающие сущности, 128 отношения небииарные, 62 отношения неидентифицирующие, 120 графическое предстааление. 128 нарушения ЗНФ, 184 обязательные и необязательные. 121 отношения необязательные, 122 Information Engirwerfajj, 132 графическое представление, 118 должны быть неидентифицирующими. 122 отношения обязательные обеспечиваемые триггерами AFTER, 403 обязательные чеидеьтифицирующив отношения. 121 отношения "один it п“, 60 ’один к одному" и “один ко многим”, 77 отношения “един к X" таблица, показывающая возможные мощности, 123 отношения “один и одному', И отношения “один иодному или большему числу", 123 отношения “один ко многим”, 60,11# Enterprise Manager, средство построения диаграмм, 134 отношения подтипов. 127 графическое представление. 119 учебный пример, 141 отношения рекурсивные, 51,125 графическое представление. 119 отношения само-ссылающиеся, 61,125 графическое пред ста а пение, 118 отношения тройные, 62,183 5НФ, 195 разрешение, 186 семенные е 4НФ, 182 отчет банка bank ссразец как источник информации со атрибутах, 91 отчеты, формирование, 207,235 ежедневные данные, основанные на QDS, 12 использование копии БД, чтобы улучшить карвкгористики, 236 кгвочевыв проблемы а физгиасязм гфректировввки. 235 объемы данных и реализация 235 проблемы при сложном южп, а? простые требования для ВД талые для ч-ения, 614 реализация ВД топы» дан чтения, 667 сложность от четса и реал иэация, 237 стандартные и специализированные отчеты, 203 требования для годаержания решения, 548 требоеа-гия к данным должны быть определены на мниих стадиях, 27 требуются свсиифомтияие деюше, 238 учебный пример, 222 формирование стетов в реальном времени, используя представлен и в. 474,475 хранилища данных для оптимизации формирования отмети, 5 частота получения. 239 отчеты, формирование а реальном времени, 475 оцени» штата специалисты, требуемые в больших п рс а стах. 243 очистить и начать снова реализация БД только для чтения, 611 очистка данных, 246 ошибки см также обработке ошибок, программирование. связанные с использованием MULL. 471 ошибки обновления строк, 514 ошибки ограничст ий от триггеров вызывают отит,387 отображение дружественных сообщений 488 п пакетирование SQL-операторы, выполняемые в группе, 513 вызовы ВД через умеренные соединения, 240 рост пакетов CLAP-БД. 548,569 транзакции не депжны выходить за границы пакета. 452 гран за кци и с оператором GO. 443
пакеты с несколькими операторами, 514 «федепениа, 513 ошибки обновления втрок, 515 пакеты с одним оператором, 514 память •викостъ максимизации ОЗУ, 563 динамичеомя настройка памяти, ИВ настройка, доступные счетчики 568 нзстройкз, иессогмо экземпляров сервера, 571 настройка памяти процесса. ST2 оптимизация сервере Windows 20№, 564 оптимизация сервера ШЫаи NT, 564 работа с SQL Sw*er 7 и Ж W работа с AWE. 565 свободная пиита ОС, 570 требования t другим приложениям, 5Я требования к соединениям сервер#, 243 “паралич «налим*, 225 параллелизм см. также состязание доступа; блокировка. конфликт с цвлостностыв данных 446 максимизация с БД толвко дпя чтения, 598 управление ЗСО, 443 параметры, названные пользователем определяемые пользователем функции не допускает иаюпыоввмш, 362 паритетная информация г RAID 5>, 580 перегрузка диска, 582 перемежающиеся ссединения см. г^акке мобильные клиенты, подписка БД только дпв чтения, 613 частота обновления БД только для чтения, ОТ переменны» объявление к» тип данных cursor, 461 типы даиньве, определяемые пользователем рабс'ают противоречиво. 235 переносные устро йства см, мобильны» к л иеиты. пересечение таблиц модели, ориентируемые на решение, 606 •Пересечение*. оператор, 57 пессимистическая блокировка, МО планирование проектирования, 218 важность всестороннего документирования, 24 стадия сбора информации. 21 учебный пример 221 планировщик работы SQL Server Agent 249 планы запросов повторное использование, 520.523 повторное испояыоданив хранимых процедур 52) роль в нестройна фу н кцио ни роза н ия, 59 ' планы преобразования даниык, 217 повторение групп данных признак и арушвния ЗНФ, 167 признаки нарушения 2Н Ф, Ий повторение префиксов ключей именах полей, признаки нарушения 2НФ, 1Ш поддержка ВД см. ВД кмин толь» для чтения. модели Use Case, Ив поддержка решений ежедневная основанная на ODS, 13 основное иоипиомижх5виипищаданв«, 13 требования к отчетам а OLAP-БД. 548 поддержи транзакций в ст юна ющая белее од н ого сервера с использованием DTC, 246 целостность данных OLTP, 11 подзапросы включение В соединения, 456 пример испопмоввнив в ложен н six подзапросов вместо курсоров. +63 подзапросы вложенные пример работы без курсора. 4вЭ подписка тиражирования, 613 подписки определение тиражировали», 245 подсистема ЦП контроль, но не настройка работы, 528 подтипы причины отхода от логического прсе ктг. 262 учебный пример. 336 поиск данные, чувствительные к pfneipy, могут вызвать проблемы, 305 полнотактовый поиск, 249 полоса пропускания у менодение. используя ’толстый' юпиент. 250 пользователи см /пакт консультация с заказчиком влияние многоуровневого проекта на резные типы, 417 вданостъ ранней идентификации 27 наблюдение важных соединений гопьзогателя, 584 представление в моделях use Case, 109 ряд проблем с тассичеапйаонфм1уродвй «мент-сервер, 254 гамо-стнфигурируюп|,№ся ж-юлинения ROt Server 7, S83 транзакции воздействуют на «ев соединения пользователя, 4+3 управление доступом к данным с помощью хранимых процедур. 520 чиа» и конкуренция. 211,212 число и остимизация характеристик. 243 пользователи переносных компьютеров си. мобильные клиенты. пользовательские БД источник денормзлизоаанных данных, ВВ9 поля с несколькими частями юррвищома, 156 понижение типов курсоров, 462 портативные компьютеры at. мобильные клиенты, последовательность см. также данных целостность. противоречие с па par ле лизм ом, 449 свойства транзакции, 438 потоки приложения, не входящие в ядро, использую’ олова, 9П права см. доступ к данным. права доступа см. также права доступа к данным. задание для ОПФ. Ж прав® доступ» к данны м, 219 см. юшж владелец БД, роя». правила и.стопьзевание типов данных, -о щехгегстимих попттмм, 293 правило гарантированного доступа второе правило teflija для СУРБД ЯО ' правило «даиюствидпети даенадавтое гфмило для СУРБД, Ю4 СЕО
предварительная документация дополнительные вопросы после анализа, П ' определение сущностей во время анвтаа, 87 учебный пример, 34 предикаты, 47 правила ввода данных в таблицы S3 предикаты доменов. 147 представления, 474 йнформвционная схема как найэр предета ал е« ий, 325 использование для маскирования, 474 уу,уук, •аррвкгирсяка, испояьяуя триггеры INSTEAD OF, 416 ограниченна доступа к предстааланяс, нежели к таблицам,47в ограничения единственной таблицей. 387 определение и обсуждение испапыоввния, 51 организация абстрактных яросте»к, 217 ориентированные на таблицы ВД только для чтения, 60S проблемы с модификацией, 415 разделение данных таблицы, 477 сравнение с хранимыми процедураии, 474 трилеры INSTEAD OF используютсв не только для отдельных таблиц, 417 шестов правило -ододэ для СУРВД, 631 представления данных ей, моделирование даинык; Use Сам модели. представления индексиру еыь!е, 479 иягопьэуютсядля соданиззции сложного поиска, 238 ограничения на исхадоое содержа оде, 486 преобразования неявные. 27S преобразование типа может вызвать проблемы с вариантными данными, 287 скаляры для бинарного сопоставления, 307 типе detatfrn* в строку, 428 привилегии пользователя см. доступ к данным привилегированность доступа ск< доступ к данным приемочные испытания, 621 приложения, типы определение числа соединений одлизааодеией 584 пример БД тал и си артистов, SS8 безопасность на уровне одаоб цоа, 523 безопасность на у од «не строк. 527 доступ, иодолм у я ОПФ. 384 пример БД ресторанов, 40 пример видеотеки обработка подтипов, 262 обязательные отношения, 122 отношения классификации, 126 пример двойного сопоставления, 305 пример издания книги модели Use Case, 107,211 нарушение ЗНФ, 164 нарушения 2НФ, 168,162 пример планирования презентаций разрешение трейньж отношений. 183 пример продажи автомобилей использование итоговыхданиыхЛвТ пример проката фильмов см. пример видеотеки, пример работы банка см, учебный пример. приобретенные системы недоступность деталей реализации, 618 природные бедствия, 623 проблемы функционирования БД только рт чтения, 607 выгодные индексы, 554,556 добавление индексов на стадии настройки, 319 индексируемые представления, 479 к. ссорь-. 482 максимизация запросов а ВД только для чтений, SSS настройка, не жертвуя нормализацией, 235 неретамвнтироввниыа запросы SQc-ксда 520 нврвтвлвнтуфованж»» запросы, устраняемы» /елсп ьэоввнием представлений, 476 ограничения структуры, 212 Мненья проблемы кодирования, 453 размещение изображенай, 283 размещение полей типа text 284 распрадепвнные се«цисниод ванны* представления 480 серверы, 563 триггеры и проворш-офвничвния, ЗвЗ удаления и устройства Споки оде ки записей, 303 формирование отчетов, 235 число пользователей как фактор, 243 чтение с диодси и внешнее размещение, 559 проверяв см. проверка данных. проверка границ использование триггеров-АгI Ен, 405 провари формата поля с помощью ОПФ. 378 трудно иотижзовать тржтвры INSTEAD OF, 4'0 учебный пример. 429 промямленных вюточенив мржтабпичнык сравнений 41 в использование *галстьа'» тон «их” клиентов, 2S0 код проверки одетой строят, 382 многострочная проверка с помощыо триггеров. 392 обработка ошибок от внутренних тритrepo а, 398 правиле, 28 правиле, которые не подходят для реализации клиента, 2S2 пример зал иод номеров, каталогов, 378 провар» диапазона е помощью триггере AFTER. 4® про зерни-с граниченод. определение непраив&ных данных, ЗИ таблицы исключений, 413 проверка характеристик ем также настройка функциснированиг; тестирование. вторая стадия в разработке ВД 52# проаврки-ограничения, 375 зависимое™ между одрпб сами. 62 значение <усповия_поисм>, 376 использование провар»» диапазонов, 429 используемые прааипа должны быть иа удавив БД 418 на основа функции, 378 нарушения, специальные одебщодин об ошибках, 48! один из пята «ипр.в ограничений. 371 определение, 45 получаемые из спис-ов доменов, 425 триггеры могут работать лучше при даоверках. 383 учебный пример, 424 программа массозоге копирования см. ВСР программирование дополнительное в ненормализованных системах, 15 кодиромние Гфипожений, а на создание объвктоа, 43в кодирование триггеров, дешрвтор CREATE TRIGGER, 387 логические ошибяг из-за NUIX. 471 п₽о5лемы, связанные с нарушением 1НФ, 155 проблемы, связанны» с нарушением НФБК, 171 проблемы, связанные ©нормализацией, 163 проблемы, устраняемы» с помощью 2НФ, 160
проблемы, устраняемы» с помощью ЗНФ, 184 реляционное и процедурное, различия. 17 проектирование интегрированных ВД четыре фунедионмьнь® модуля. ю "Произведение", оператор- пример CROSS JOIN SB протокол передачи файлов, П прототип БД риск нарушить ожидания зюяз^яя, 25 прототипы отчетов выбор данных для представления 210 учебный пример, 223 процедура преобразования учебный пример, 626 процедуры фиктивные. 494 процедуры хранимые 4в1 cv такте SQ L-за п рос ы ком п и л и руем ые. RECOMPILE, опция, 584,Я1 безопасность обеспечивается на уровне об ызктоа 494 возвращаемые значения. 482 выполнение для кдждой отроки в наборе, 464 вычисивиие полного размере таблицы, 6S0 вычисление размера инде« -я. 65* инк а псуладм - основное гфввущвстио, 523 использование бизчес-праяил 418 корре ИИ ровКГ! данных, можно переписать, но продогаетютраво-тмь, 493 наборы результатов, возвращаемые оператором SELECT, 484 - не должны изменять число транзакций, 443,485 необязательно» каскадирование удалений 420 обеспечение биэнес-прввил, 422 обеспечение целостности данных, Зав обновление отдельных строк. 50В обработке вшивок, 485 обработка вшивок при вызове и» триггеров. 398 обычно» иелопыоввни*, 500 ограничение путей доступа к данным, 6200 параметры раСогы, 502 повторное иствпьаоваиив планов запросов, S20 с а аризация ВД только для чтения, 610 реализация с домена м и столбцов, 46 следует избегать азеимсдействий с клиентом, 485 создание отдельны* строг., 506 специвльные, доя учебного примера, 5*0 сравнение с ОПФ, 350,481 сравнение с пакетами, 481 сравнение с г-редстаэленичми 474 удаление отдельных строк, 511 усечение объединенной таблицы, 626 учебный пример, 53-3 процесс экспертнойоиенки. 206 процессоре время настройка для яыоо«ог.рисритогиых и фоновых приложений, 573 процессы определение те до мументации. заказчика, 84 получение в записях интервью с заказчиком, 88 таблице новых процессов в примере байка, 99 процессы основные, 83 процессоры, родственность, 574 псевдонимы типы данных, определимые пользователем, 292 публикации определение тиражирования» 245 пустые стреми проверка, использующая ОПФ, 378,424 пул соединений организация в бизнесдлое п-звеинсй системы. 254 техника для мнвг-олальзовяяпьсяа еиетвм, 243 Р разгрузке данных большие ле седан и данных могут бЫТэ разбиты на части, 241 разделение дисков i RAID), 578, 5В1 разделители Обозначение в квадратных скобках. 257,338 ограничения обозначений, 321 признак, что существующие данные не удовлетворяют 1Н®, 1Й соглашение ов обозначениях в SCI Server 267 размер ВД см, данные, объем: объемный анализ, размерности ограничения для таблиц БД, 268 размеров ограничений компоненты таблиц БД. 268 “Разность", оператор NOT IN. оператор SCI.. 58 разработка БД итерационный процесс, 620 разработка и логическое проектирование определение процессе риравотвд, 66 стратег»» разработки отчетов 268 разработчики связь с разработчиками, 325 сирость, используя «ерегпамамтированньгй SQL-ищ, 520 разреженные таблицы. МВ разрешен не конфликтов вы, состязание доступа, распределенное вычисление кпасприь» серверы, 244 обычно включается в приложения БД, 13 распределении» системы, йб- расл редела иные запросы, Б32 распределенные секционированные представления, 48В расширенные свойства определение и добаштени» программным способом, 330 реализация задачи, которые нужно решить перед началом рвапиаит 2М отличие от гютчбйио проектирования, 234 реализация, проблемы денормапизация. 196 ислог-ьзован не итоговых данных. 167 конфигурация сети, 239 - ... ненормализованные системы приводят < излишнему пногрзммидоеаыгю, 151 разбиение страниц, 31Й ус-paнение испопьэаваннем нредстаален«й, 474 реальные данные мзраб.отк:а ВД на должна гяпучвше отчетов, использун предстааления 474 ревизии перенести формиродаиие протоколов ревизий ня отааи» фижчвйого лроо«тирова иия, 7й трвбоввиж,30 регистр Ввнка атрибуты, получаемые я»ашиор№Мй№фёрМ47% 92
реорганизация БД копирование с большим объемом данный, 238 роли (fittonteusent), 524 коды ошибок, 383 специальное использование таблицы для размещения информации, 488
тупиковая ситуация, заданная номером сшибки 1208,451 храни мыв процедуры возвращают значения и сравнения об ошибках. 4®1 сообщения об ошибках информативные, 48 8-4S9 сообщения «6 ошибках специальные, 459 нарушения гроаерскюграяичений 489 тег значений для включения в сообщение. 4П сопоставление типов, 304 см. давив сортировка. учебный пример, 353 сортировка С.‘И ГПЭКЖё тиньв использование курсора для искусственной сортировки, 464 состязание см. состязание доступа. состязание доступа см. тают параллелизм. минимизация БД толы» для чтения, 599,607 нале-паментирзванные запросы пользователя как источники. 238 : следует избегать в сильно распределенных системах. 256 специализированные отчеты, 208 список объекте» учебный пример, 83 список элементов управления заполнение, пример хранимой процедуры, 500 среда хранения стратегия архивирования и вреде размещения 560, S63 средства формирования отчетов объекты БД, 325 подходы, ориен-ируемые не таблицы, позволяют легче использовать, 604 средства хранения стратегия архизирозания. Ш ссылочная целостность внешние ключи, 534 между ВД с помойте триггеров, 386 тоензакции OLTP, 12 стадия разработки требования к аппаратным средствам. 621 стандартные отчеты, 208 стандартный международный номер книги см ISBN, старый код вы, наследуемые системы; исходные системы. статистика ввода-'вывода, 597 использование дпв тевгирования, S01 степень оуношаиия, 42 столбец тала bit оператор GROUP BY, использующий тал данных In у; nt. 274 столбцы атрибуты, 42 «голове и тала, 52 индексирование всех в БД только для чтения, 607 вторые дорого обновлять. 510 максимально допустимое число, 269 нормализация уменьшаетчисло 152 обладает поргщам, 42 ограничение втдаииости с помощью представлений. 477 определение SQL Setw, 42 определение, используя CREATE TABLE. 270 отлична от атрибутов, 74,112 физические и гычисляемье 270 столбцы виртуальные си. столбцы вычисляемые, столбцы вычисляемые, 270, 298 построение ВД топы» для чтения. 603 столбцы идентификации CREATE TABLE, одаржюр. 2'6 столбцы и денти ф икаиии, иачалыяе значение, 27» столбцы иде нтифи каци и, приращение, 276 страниц передача число зависит от инджио*, 151 страниц разбиение использование кластерных индексов. 310 страница, размер программный доступ, №1 строк число ©дуох-сост! переменная после выражений SET, 384 использование термина "мощность*, если число неоднозначно, 50 строки доступ к конфетным строкам с помощью ©рсара, 464 максимальное число байт на строку, 269 ограничение видимости с гомошь-о представлений, 476 определение в SQL Server, 41 строки, идентификаторы гвбежани» двойных строк, 47 искусе таенные ключи, 49 строки текста ем. запись стропе слои прописными буквами: символьные данные. структура корпоративных данных четыре функциональных модуля, 10 структура организации пример рекурсивного отношения, 12В структура системы требования, предъявляемые жВД, 212 часто сложная, с различными скорвстямм соединения, 242 СУБД (система управления базой данных), 5 СУРБД (системе управления реляционной базой данных), 52 сущности, 41 атрибуты как идентификаторы, 72 все атрибуты должны быть описаны в 2НФ и ЗНФ 168 добавление рписиепьной информации, IX должны быть отношениями и посвящены одной тема, 204 идаи как абстрактные сущности, 69 концеп г уз-ьные понятия таблиц. 4Q обеспечение целостности сущ нести 634 обозначение, 111 определение данных о зака униках 67 получение из документации заказчика в? потомки и предки офвдгямйг. 118 представлен,» в стандарте SEF1X, 1W, 112 примс-ры твбпиц с атрибутами и яжввМ), 75, 83 пример -а бл ицы для пост авщика офиснс-о оборудован»!, ТО, Ж 9 расширение для БД, формиругааядаметы, 601 езкь е отношениями, 68.11в ереймво построения диаграмм Enterprise Manager, 134-135 строки а 1 НФ должны имел, одно и » ш ямою значений, 166 таблицы БД должны предсгавдатв отдапвйыв темы, 152 удаление несущественных перед реализацией, 340 учебный r.pmep, 67,140
сущности доменов см таблицы доменов сущности зависимы», 1W внешние ключи предс-аапен ы а первичном кличе, 103 сущности идентификаторо-зависимые, 109 сущности независимые, 109 внешние ключи отсутствуют для первичных ключей 108 сущности общие отношения (шассификации 127 сущности разрешающие 129 сущности специфические отношения классификации, 127 сущность person (человек) пример НФЖ, 169 схема "звезда", 14,546. 548 схема "снежинка" 14 сценарии иллюстрация аозможнЫ* фивичаоих реализаций, 233 т таблицы архивирование с разделением сю датам, 581 доступ внутри ОПв, 364 запросы могут иметь доступ к ним, 2В0, 458 концептуальная версия сущностей, 4ф-, математически описываются как отношения 39 объединение с различными гидами операторов, 54 оператор CREATE TABLE, 2вв обрабо'кэ иски ючений, 413 отношения между таблицами, типы, 7В постоянные данные должны быть размещены внутри таблиц, 629 разделан» структуры с помощью представлений, 477 реляционная теория БД, 39 рост OLAP-таблиц, 548 роет OLTP-таблиц 547, 548 роет тввлщ в учебном примере, 588 соглашения об обозначениях, 287 создание таблиц БД, 250 терминология, иллюстрируемая примера*, О факторы, учитываемые в расчете объема. 549 таблицы доменов, 121 изменения для поддержания таблиц фактов, 561 проблемы, вызываемые взаимодействием с внешними системами. 214 учебный пример, ЗЖ 634 характеризуются медленным ростом или eta отсутствием, 547 таблицы измерений и таблицы фактов, 14 таблицы исключений, 413 таблицы ’тенкиеГ, 152 таблицы фактов архивирование характеристик строк, 562 и таблицы измерений, ;4 < период архивирования, 581 таблицы-потомки задание внешним иночвм значений NULL, 407 проблемы удаления записей-предков. 243 тег, формат расширенные сообщения об ошибках. 48В, 489 телефонов номера обновление гадов региона, элементарность и телефонные номера, 155 примеры нарушения 1НФ, 153 проблемы, связанмув с различными форматами. 78 учебный пример, 176,424 терминология с.м na»«s обозначения, соглашен и я. SQL-терминвлошя предпочтительней реляционной, 63 таблицы 5Д и реляционная теория, 39 таблица соответствия ЗЯн-терминов нтерминов реляционной теории. 64 управление работай 566 тестирование см. такт настройка функционирования; проверка характеристик. защита данных тестированием триггерами, 392 работа хранимых процедур, 531-502 тиш тестирования, 620,821 техника интервью, 26 тип данных ЫдМ, 273 тип данных bit. 273 тип денных boolean испс льзоаание тапа small л t мало подходит, 274 тип М км лучшая аппрогаиввция в SQL Server. 273 тип данных d a teti те, 279 случаи, ко'да определяемые пользователем тапы данных пред почтит ельны, 2вв удаление времени с помощью ОЛ®. 428, 476 удаление времени с помощью триггере INSTEAD OF. 431 удаление часов, используя системную функцию вМеРаП,47в фактически няруаиет 1НФ, 158 тип данных decimal, 275 тип данных money, 276 тип данных real, 278 тип данных rowversion at. timestamp, столбцы, пт данных smalldatetime, 276 тип данных small int, 273 тип данных sma 11 money, 276 тип данных sq Invariant, 285 определение тапа даиных е кмотьп хранимой процедуры, 2В7 размещение расширенной описательной информации, 330 тип данных string общий тип данных в проввркж-ограничениях, SK тип данных «увпатв, Й9 тип данных table не допускается как тип данных столбца, 292 тип данных text, 284 тип данных tinyint, 274 тип даиных uniqueidentifier, 317 задание итогенарации о помощью rowguidcol, 280 тип данных varbinary, 282 тип данных varchar, 284 типы величин нельзя представить типом float, 278 типы данных выбор, состаетству ющий логическим доменам, 272 свпопнитеп., србщий_тип_да»нт,х>. 372 курсор, объявленный как переменная 460 наследование a !DE FIX, использование доменов. 11В обеспечение безопасности дигных, 358 общие классы евжиы для .логического проектирования.44 определяемые пользователем, 45 преобразование логических доменов, 346 примеры домен» в SCI Server, 44 таблица •прикитианьвс’ типов данных, 44 тип corso-'. передаваемый как выходной параметр, 484
типы данных neftar, п varchar и ntext. 285 типы данных встроенные. 272 типы данных двоичные, 282 типы данных общие, 44 строят в проверках-»раиичениях, 376 типы данных, определяемые пользователем, 45, М2 нельзя изменять, когда исйссыустся, 296 работа с датами и аременем. 288 работа с перемеииыми пэстивсречиаа, 295 типы курсоров, 481 важность ТОЧНОГО задания, 488 влияние на сортировку, пример 467 поникание типа* 488- тн рэжирование, 245 альтернатива доступу к данным между БД. 531 может потребоваться для диалогового доступа 240 распределенные секционированные првдсямшния, 480 реализация БД только для чтения, 610,611 тиражирование копии,245 ти датирование с помощью транзакции, 245 подходитдня удалвнньшВД, 613 тиражирование слиянием,245 рододеделенные систвмы, 256 . . "топстый’* и “тонкий" шкал, 250-251 топологии см. топологии сетей. твпологии сетей, 239 см. также n-уровневая конфигурация; системы клиент-сервер. - клиент и конфигурация даням:, 253 предназначены для минимизации проблем функционирования, 238- - скорость соединения для доступа клиент-сервер. 240 трехуровневые и п-урмимыв системы, 2S4 точки сохранения, 442 задание с помощью хранимой процедуры, 4а 7 точность задание требований для чиииаых типов данных, 275 тип денных decimel, 275 транзакции, 438 блокировке кек механизм управления 443 использование завершения с двумя стадиями, 532 лучше иметь коротки» и а одном пакет*, 448,452 лучше делать короткие, чтобы минимизировал,, тупиковые ситуации, 451 модифицирующие операторы должны: быть внутри, 384 определение, 12 определение длительной работы 584 откат воздействует на асе триггеры 387 отит после тда тестирования, 392 оши&ж хранимых процедур. связанные с числом, транзакций. 495 пакеты с несколькими операторами, 514 пос ледемтепьности заве рше н и я пакетов а случае ошибок, 516 частичное, ивершение, Ий транзакции длинные минимизация воздействия, 452 требования поиска проблемы формирования отче тов из-за слохтого поиск», 238 трехзначная лопни, 468 трехуровневые конфигурации. 254 1 рек фазная модель разработка БД, проверка характеристик и реализация, 620 триггеры, 38В см. также SQ L-ко д компилируемый. доступ к данным между БД, 531 запрещение триггеров нарушает двенадцатое правило Кодае, 635 --, . . значения, сбеспечиаающие целостность данных, 418 индексируемые фвдстввтния, как альтернатива, 479 использсзание оптимистической блокировке. -301 использование обработки сшибки, 387 исходные дачные должны удовлетворять ограничениям, 403 короектирозка касхадиоднания коодрая требует триггера, 326 мн подстрок на я проверка, 392 . могут работать лучше, чем проверыт-ограничекия. 383 неоЫдаыо проверяжвля^.втра1«а»#ио, 382 несколько триггеров для одной операции, 394 несколько триггеров следует использовать осторожно, 402 . . - обеспечение целостности данных, Э5В - определение последующих требований процесс» проектирования, 540 основной формат, 40а ; - осторожно вмтючать «мдаиэды, аааадащаищиа наборы результатсе. 38в отношения между БД, ЗМ порядок выполнения, 4(й проверка вло» енным и рекурсией ь.х триггеров. 395 проварка соответствия ДР-вдав и горадж, 432 реализация с даманамисторгодц 46 реализованные отношения, 135 - сравнение с Ограничениями, 371.399 удаоение времени иа величин тити detotime, 431 учет межтабяичнъа зависимостей, 53 учет межстрочных зависимостей, 53 цикл хранимых процедур «к ал ьодрнатиаа, 484 триггеры вложенные, 394 триггеры, порядок выполнения, Ш тупиковые ситуации, 450 У удаление в таблице-предке установление вне»»» ключей потомка в NULL, 407 узкие места возможные принты, W ' диск и ЦП, 575 работа даоса, 582 узлы-листья, S89 кластерные и неюжсяерныё индексы, 553 указания оптимизатору выбор т.ОДОВ блокировки, 444 набегание блокировок, тыт лредстаявния. 476 использование конкретных уровней изоляции, 449 перечень, 440 указатели на расположение адресе «к примеры атрибутов 74 указатели, страницы и строки, 553 уникапьные идентификаторы uniqueidentifiers at также GUID. timestamps как уникальны»-величины, 288 а качестве ключей а НФБК, 1628 од- уникальные ограничения. 371 испс-льзование со вторичными изявд 313,31Т унифицированный язык моделирования см. UML. унифицированный указатель информационного ресурса, 75 ©и. URL.
управление работой (памяти), 565 как искусство, 666 уровень изоляции ло умолчанию *фивфОваиноачт**в||=‘, --5,449 урммь изоляции #Повторв6Н04 чтение#, 445,447 уровень изоля ц и и "Свободное чтаниещ 445 уровень изоляции "Упорядочиваемый". 446,445, уровень изоляции ^Фиксированное чтение#, 445 уровень корневого узла определение при вычислении размера индекса, 557 уровни изоляции продолжительность, блокировки. 445.448 задание утаений оптимизатору. 448 уровни обслуживания планирование. 623 соглашения об уровне с-бспужиеания, 30 урмии серьез пост и, сообщения «б ошибках, 323 условные добавления использование триггеров INSTEAD OF, «3 устойчивость к ошибкам обеспечиваемая комбинациями RAID, 578 устройстве блокировки записей, 303 учебный пример (проверка регистра) внешнее системы, 224 испспыованив бизнес-праэил. 423 использование данных и фор м и ро вание отчетов, 220 мсделирюаение в Usa Cat», 138 моделирование данных, 140 модель данньос поена НФБК, 179 модель ран пых после полной нормализации. 203 нарушения 1НФ, 172 нарушения 4 НФ 197 н еобязател ьные бизнес-правила 435 НФБК, 174 о щан и-чен ия по умолчанию, 423 определение, 16 определение сущностей, 85 объемный аиалив, г2й, 588 подтип сделки, ЗЗв пометка атрибутов документа, 88 построение- структур доступа к БД, 533 построение физической рее лизании, 335 проблемы безопасности 542 провари диапазонов, 429 . проверки,ограничения и ОПФ, 424 проста» топология клиент-сервер, 256 процедура заполнения доменов. 538 процедура формирования списка 538 служебные столбцы,342 создание БД для формирования отче тог, S24 список объектов, 83 стадия свора информвции, 31 таблица доменов, 14S таблица сущностей, атрибутов и Д оме н-os. вЗ типы данных. 346 фкиичвекая модель, 353 формирование отметав, 222 хранимая процедура ойноаления. 535 хранимая процедура размещения, Я4 хранимая процедура удаления, 537 Ф файл определения паша, 355 ем. PDF фактор заполнения, 552 факты корректировка записей с переменным числом фмто», 157 <|>изичвс131я учебный пример, 353 физическая независимость данных восьмое правило Кодда дав СУРБД, 632 иллюстрирующие сценарии, 233 физическое чтения параметры работы, 502 формат поля формир-змни» проверок с ОПФ, 378 формат семантического моделирования EnoiehQuwy, 615 использование SQL Prof-er, 332 формы разрозненные источники данных, 31 функции использование в ограничениях по умолчание. 373 функции встроенные ОПФ не могут некоторые дених включат,, 370 функции едетермииирсваииыа, 370 функции в несколькими операторами, формирующие таблицу, 360 функции е одним оператором, формирующие таблицу, 365-356 функциональныезависнмости, 6S 2НФ. Ш белее высокие норма л изо ванные форм „г 153.161 не ключевые атрибуты в ЗНФ, 164 функция update, триггеры, 389 X "характеристики ползут" роль докумеНтацИИ. чтобы и’бежать, 24 хранилище данных 13 альтернатива ЕД только для чтения. 589 диаграмма типичной, структуры, 16 ж средство петоержания решения, 13 с-лтимизированнсе размещение для оргениищии ог-«тс-5.9.13 протааомложность к ОчТР, 9 хранимые процедуры, специальные вычисление полного размера таблиц 550 вычисление размера индекса 654 хранилище данных в противогаз ложность, OLTP, 9 как средство поддержи» решений, is поддержка БД тальке для чтения является альтернативой, 599 размещение оптимизированное для формирования отчетов, 9,13 схема типичной структуры, 16 хранимая процедура тиражирования реализация ВД талые для чгвмя, 610 хранимая процедура удаления, 511 ограничен ия обозначений. 321 хранимые процедуры обновления столбцы, дорогие дая обновления, 510 хранимые процедуры системные си, ,’паюие sp_; процедуры хранимые. иьформациенная схема является апиврнатимА 326 услсеия использования курюс-роэ для выпепнвния. 404
Ц целевая свободная память, 670-571 целые числа тап данных Н, 273 увеличение скалярной ОПФ, 361 ЦП (центральный процессор), 236 ч чеки (банковская система) образец как источник информации ов атрибутах, 89 числовые данные приближенные, 277 чтение с дисков можно определить допустимую работу, 559 “что-то в промежутке” идеальная системе итивнт-еервер, 252 чувствительность к регистру ои. тате запись в строке слое прописными буквами. изменение оопостамания для задания, 305 э обычно используются кас хранилища данных, в£И проблемы целостности данным. 28 атрибуты, требование 1НФ. 1-53 доступ к внешним твбпицм», 418 корректировке телефонных кодов регионов, 156 необязательные каскадирование удалений, 420 свойства тракввиии, лзв транзакции OLTP, 12,<38 учебный пси мер. 172 представление битовой нас»», 391
Справочное издание Дэвидсон Луис Проектирование ваз данных на SQL Server 2000 Редактор Б, Копылов Художественный редактор О. Лапхо Компьютерная верстка Л. Катдркшиза Лицензия иа издательскую деятельность № 06331 от 21 ноября 2001 г. Подписано в печать 80.04.03. Формат 74х100Ъ1в. Гарнитура Школьная. Бумага офсетная. Печать офсетная. Усл. иеч. л. 58. Тираж 3000 экз. Зак»18б8 Издательство «ВИНОМ. Лаборатория знаний». Адрес для переписки: 119071, Москва а/я 32, Телефон (095)955-0398. E-mail: lbz@aha.ra Отпечатано с готовых диапозитивов в полиграфической фирме «Полиграфист» 160001, г, Вологда, ул. Челюскинцев, 3.