- •Глава 2 Базовые понятия реляционной модели данных 21
- •Глава 3. Целостность реляционных данных 28
- •Глава 4. Реляционная алгебра 39
- •Глава 6. Нормальные формы отношений 57
- •Глава 7 Нормальные формы более высоких порядков 74
- •Глава 8. Элементы модели «сущность-связь» 83
- •Глава 5. Элементы языка sql 110
- •Ранние подходы к организации бд. Системы, основанные на инвертированных списках, иерархические и сетевые субд. Примеры. Сильные места и недостатки ранних систем
- •Основные особенности систем, основанных на инвертированных списках
- •Структуры данных
- •Манипулирование данными
- •Ограничения целостности
- •Иерархические системы
- •Иерархические структуры данных
- •Манипулирование данными
- •Ограничения целостности
- •Сетевые системы
- •Сетевые структуры данных
- •Манипулирование данными
- •Ограничения целостности
- •Достоинства и недостатки
- •Элементы теории множеств Множества
- •Операции над множествами
- •Декартово произведение множеств
- •Отношение
- •Примеры отношений Бинарные отношения (отношения степени 2)
- •Отношение эквивалентности
- •Отношения порядка
- •Функциональное отношение
- •Еще пример бинарного отношения
- •Транзитивное замыкание отношений
- •Глава 2 Базовые понятия реляционной модели данных Общая характеристика реляционной модели данных
- •Типы данных
- •Простые типы данных
- •Структурированные типы данных
- •Ссылочные типы данных
- •Типы данных, используемые в реляционной модели
- •Отношения, атрибуты, кортежи отношения Определения и примеры
- •Свойства отношений
- •Первая нормальная форма
- •Глава 3. Целостность реляционных данных
- •Null-значения
- •Трехзначная логика (3vl)
- •Потенциальные ключи
- •Целостность сущностей
- •Внешние ключи
- •Целостность внешних ключей
- •Замечания к правилам целостности сущностей и внешних ключей
- •Операции, могущие нарушить ссылочную целостность
- •Для родительского отношения
- •Для дочернего отношения
- •Стратегии поддержания ссылочной целостности
- •Применение стратегий поддержания ссылочной целостности
- •При обновлении кортежа в родительском отношении
- •При удалении кортежа в родительском отношении
- •При вставке кортежа в дочернее отношение
- •При обновлении кортежа в дочернем отношении
- •Глава 4. Реляционная алгебра Обзор реляционной алгебры
- •Замкнутость реляционной алгебры
- •Отношения, совместимые по типу
- •Оператор переименования атрибутов
- •Теоретико-множественные операторы Объединение
- •Пересечение
- •Вычитание
- •Декартово произведение
- •Специальные реляционные операторы Выборка (ограничение, селекция)
- •Проекция
- •Соединение
- •Общая операция соединения
- •Тэта-соединение
- •Экви-соединение
- •Естественное соединение
- •Деление
- •Примеры использования реляционных операторов
- •Невыразимость транзитивного замыкания реляционными операторами
- •Кросс-таблицы
- •Реляционное исчисление
- •Кортежные переменные и правильно построенные формулы
- •Целевые списки и выражения реляционного исчисления
- •Реляционное исчисление доменов
- •Глава 6. Нормальные формы отношений Этапы разработки базы данных
- •Критерии оценки качества логической модели данных
- •Адекватность базы данных предметной области
- •Легкость разработки и сопровождения базы данных
- •Скорость операций обновления данных (вставка, обновление, удаление)
- •Скорость операций выборки данных
- •Основной пример
- •1Нф (Первая Нормальная Форма)
- •Аномалии обновления
- •Аномалии вставки (insert)
- •Аномалии обновления (update)
- •Аномалии удаления (delete)
- •Функциональные зависимости
- •Определение функциональной зависимости
- •Функциональные зависимости отношений и математическое понятие функциональной зависимости
- •2Нф (Вторая Нормальная Форма)
- •Анализ декомпозированных отношений
- •Оставшиеся аномалии вставки (insert)
- •Оставшиеся аномалии обновления (update)
- •Оставшиеся аномалии удаления (delete)
- •3Нф (Третья Нормальная Форма)
- •Алгоритм нормализации (приведение к 3нф)
- •Анализ критериев для нормализованных и ненормализованных моделей данных Сравнение нормализованных и ненормализованных моделей
- •Oltp и olap-системы
- •Корректность процедуры нормализации – декомпозиция без потерь. Теорема Хеза
- •Глава 7 Нормальные формы более высоких порядков
- •Нфбк (Нормальная Форма Бойса-Кодда)
- •4Нф (Четвертая Нормальная Форма)
- •5Нф (Пятая Нормальная Форма)
- •Продолжение алгоритма нормализации (приведение к 5нф)
- •Глава 8. Элементы модели «сущность-связь»
- •Основные понятия er-диаграмм
- •Нормальные формы er-схем
- •Более сложные элементы er-модели
- •Получение реляционной схемы из er-схемы
- •Пример разработки простой er-модели
- •Концептуальные и физические er-модели
- •Внутренняя организация реляционных субд Cтруктуры внешней памяти, методы организации индексов
- •Хранение отношений
- •Индексы
- •Хэширование
- •Журнальная информация
- •Служебная информация
- •Управление транзакциями, сериализация транзакций
- •Транзакции и целостность баз данных
- •Изолированность пользователей
- •Сериализация транзакций
- •Методы сериализации транзакций
- •Синхронизационные захваты
- •Гранулированные синхронизационные захваты
- •Предикатные синхронизационные захваты
- •Тупики, распознавание и разрушение
- •Метод временных меток
- •Журнализация изменений бд
- •Журнализация и буферизация
- •Индивидуальный откат транзакции
- •Восстановление после мягкого сбоя
- •Физическая согласованность базы данных
- •Восстановление после жесткого сбоя
- •Глава 5. Элементы языка sql
- •Предварительные сведения о работе с sql сервером.
- •InterBase сервер
- •Выполнение в ibConsole
- •Р егистрация псевдонима (алиаса).
- •Пользователи.
- •С оздание модельных бд.
- •Сеанс sql
- •Структура учебных баз данных
- •Операторы sql
- •Операторы ddl (Data Definition Language) - операторы определения объектов базы данных
- •Операторы dml (Data Manipulation Language) - операторы манипулирования данными
- •Операторы dql (Data Query Language) – операторы запросов к данным
- •Операторы dcl (Data Control Language) - защиты и управления данными
- •Основные типы данных
- •Строки фиксированной длины
- •Строки переменной длины
- •Числовые значения
- •Десятичные значения
- •Десятичные значения с плавающей точкой
- •Значения даты и времени
- •Буквальные значения
- •Значения null
- •Значения типа boolean
- •Пользовательские типы данных
- •Типы данных InterBase
- •Управление объектами базы данных
- •Что такое объекты базы данных?
- •Что такое схема?
- •Поля и столбцы
- •Оператор create database
- •Синтаксис
- •Примеры
- •Оператор create table
- •Синтаксис
- •Примеры
- •Ключевое слово storage (в InterBase не действует!)
- •Соглашения о присвоении имен
- •Команда alter table
- •Синтаксис
- •Примеры
- •Модификация элементов таблицы
- •Добавление столбцов, требующих обязательного ввода данных
- •Пример:
- •Изменение столбцов
- •Создание таблицы на основе уже существующей
- •Удаление таблиц
- •Условия целостности
- •Ключевые поля
- •Требования уникальности
- •Внешние ключи
- •Атрибут not null
- •Использование условий проверки
- •Удаление условий
- •Определение представлений
- •Оператор create view (InterBase) Описание
- •Синтаксис
- •Примеры
- •Определение привилегий
- •Оператор grant (InterBase) Описание
- •Синтаксис
- •Примеры
- •Вопросы и ответы
- •Практикум
- •Примеры
- •Манипуляция данными
- •Обзор возможностей манипуляции данными
- •Заполнение таблиц новыми данными
- •Ввод данных в таблицу
- •Ввод данных в определенные столбцы таблицы
- •Ввод данных из другой таблицы
- •Ввод значений null
- •Обновление уже имеющихся данных
- •Обновление значений одного столбца
- •Обновление нескольких столбцов в одной или нескольких записях
- •Удаление данных из таблиц
- •Примеры использования операторов манипулирования данными
- •Знакомство с запросами
- •Что такое запрос?
- •Оператор select
- •Синтаксис оператора выборки данных (select)
- •Синтаксис оператора выборки
- •Синтаксис
- •Примеры
- •Ключевое слово select
- •Ключевое слово from
- •Использование условий для отбора данных
- •Сортировка вывода
- •Учет регистра символов
- •Примеры простых запросов
- •Подсчет записей в таблице
- •Получение данных из таблиц других пользователей
- •Псевдонимы столбцов
- •Упражнения
- •Операции в условиях для отбора данных
- •Что такое операции в sql?
- •Операции сравнения
- •Равенство
- •Неравенство
- •«Меньше» и «больше»
- •Примеры комбинирования операций сравнения
- •Логические операции
- •Использование операторов exists, any, all, и some Описание учебной базы данных
- •Операции конъюнкции и дизъюнкции
- •Отрицание условий с помощью операции отрицания
- •Неравенство
- •Деление
- •Комбинирование арифметических операций
- •Вопросы и ответы
- •Подведение итогов по данным запроса
- •Что такое итоговые функции?
- •Функция count
- •Функция sum
- •Функция avg
- •Функция max
- •Функция min
- •Описание
- •Синтаксис
- •Примеры
- •Описание
- •Примеры
- •Описание
- •Синтаксис
- •Примеры
- •Описание
- •Синтаксис
- •Примеры
- •Описание
- •Синтаксис
- •Примеры
- •Сортировка и группирование данных
- •Зачем группировать данные?
- •Ключевое слово group by
- •Группирование выбранных данных
- •Создание групп и использование итоговых функций
- •Представление имен столбцов числами
- •Ключевое слово having
- •Объединение таблиц в запросах
- •Отбор данных из нескольких таблиц
- •Типы связывания
- •Компоненты условия связывания
- •Связывание по равенству
- •Естественное связывание
- •Использование псевдонимов для имен таблиц
- •Связывание по неравенству
- •Внешнее связывание
- •Рекурсивное связывание
- •Связывание по нескольким ключам
- •Вопросы связывания
- •Использование связующей таблицы
- •Декартово произведение
- •Вопросы и ответы
- •Практикум
- •Упражнения
- •Использование подзапросов
- •Что такое подзапрос?
- •Подзапросы в операторе select
- •Подзапросы в операторе insert
- •Подзапросы в операторе update
- •Подзапросы в операторе delete
- •Подзапросы внутри подзапросов
- •Связанные подзапросы
- •Объединение запросов
- •Обычные и составные запросы
- •Зачем использовать составные запросы?
- •Команды построения сложных запросов
- •Команда union
- •Команда union all
- •Команда intersect
- •Команда except
- •Использование order by в составных запросах
- •Использование group by в составных запросах
- •Обеспечение правильности результатов
- •Примеры использования оператора select
- •Отбор данных из одной таблицы
- •Отбор данных из нескольких таблиц
- •Использование имен корреляции (алиасов, псевдонимов)
- •Использование агрегатных функций в запросах
- •Использование агрегатных функций с группировками
- •Использование подзапросов
- •Использование объединения, пересечения и разности
- •Синтаксис соединенных таблиц
- •Синтаксис условных выражений раздела where
- •Порядок выполнения оператора select
- •Стадия 1. Выполнение одиночного оператора select
- •Стадия 2. Выполнение операций union, except, intersect
- •Стадия 3. Упорядочение результата
- •Как на самом деле выполняется оператор select
- •Оператор соединения
- •Оператор пересечения
- •Оператор деления
- •Использование индексов для ускорения поиска данных
- •Что такое индекс?
- •Принцип работы индексов
- •Команда create index
- •Типы индексов
- •Простые индексы
- •Уникальные индексы
- •Составные индексы
- •Простые и составные индексы
- •Неявные индексы
- •Когда следует создавать индекс?
- •Когда не следует создавать индекс?
- •Удаление индексов
- •Повышение эффективности работы с базой данных
- •Что означает оптимизация операторов sql?
- •Оптимизация базы данных и оптимизация операторов sql
- •Форматирование операторов sql
- •Форматирование операторов для лучшего восприятия
- •Правильный порядок таблиц в выражении from
- •Правильный порядок условий связывания
- •Наиболее ограничительное условие
- •Полное сканирование таблиц
- •Когда и как избегать полного сканирования таблиц
- •Другие аспекты оптимизации
- •Использование like и знаков подстановки
- •Замена операций or выражением с ключевым словом in
- •Недостатки использования выражения с ключевым словом having
- •Долгие операции сортировки
- •Использование готовых процедур
- •Отмена использования индексов в больших пакетных операциях
- •Средства для анализа производительности
- •Создание и использование представлений и синонимов
- •Что такое представление?
- •Использование представлений для защиты данных
- •Использование представлений для управления выводом данных
- •Хранение представлений
- •Создание представлений
- •Создание представления для данных одной таблицы
- •Создание представления для данных нескольких таблиц
- •Создание представления на основе другого представления
- •Уровни зависимости представлений
- •Опция with check option
- •Опции cascaded и local
- •Синтаксис
- •Примеры
- •Обновление данных представления
- •Представления и выражение order by
- •Удаление представлений
- •Что такое синонимы? (InterBase не поддержвается)
- •Управление синонимами
- •Создание синонимов
- •Удаление синонимов
- •Триггеры и хранимые процедуры (InterBase) sql для триггеров и хранимых процедур в InterBase
- •Обработка исключений
- •Обработка ошибок sql
- •Обработка ошибок InterBase
- •Комментарий
- •Триггеры и их назначение
- •Синтаксис create trigger
- •Примеры
- •Дополнительные сведения по работе с генераторами
- •Хранимые процедуры и их назначение
- •Процедуры для работы с датой и временем
Дополнительные сведения по работе с генераторами
Создание генераторов
Генератор – это специальный объект базы данных, который генерирует уникальные последовательные числа. Эти числа могут быть использованы в качестве идентификаторов (например код клиента, номер счета и т. п.). Для создания генератора необходимо использовать оператор DDL
CREATE GENERATOR generatorname;
При выполнении такой команды происходит 2 действия:
На специальной странице БД отводится 4 байта для хранения значения генератора
В системной таблице RDB$GENERATORS заводится запись, куда помещается имя генератора и его номер (фактически смещение на странице генераторов).
После создания генератора его значения можно получать при помощи функции
GEN_ID(generatorname, inc_value), где inc_value – число, на которое необходимо прирастить значение генератора.
Генераторы возвращают значения (и сохраняют свои значения на диске) вне контекста транзакции пользователя. Это означает, что если генератора было увеличено с 10 до 11 (инкремент 1), то даже при откате транзакции (ROLLBACK) значение генератора не вернется к предыдущему. Вместе с этим гарантируется, что каждому пользователю будет возвращено уникальное значение генератора.
При выборке значения генератора запросом вида select gen_id(genname, x) from ... следует учитывать буферизацию выборки на клиенте. Т. е. в многопользовательской среде при выполнении двух таких запросов значения генератора будут увеличиваться «пачками», а не на величину x для каждой выбираемой записи.
Использование генераторов в триггерах и хранимых процедурах
Пример триггера, автоматически присваивающего уникальное значение ключевому полю таблицы:
Создадим генератор для уникальной идентификации клиентов:
CREATE GENERATOR NEWCLIENT;
Создадим триггер для таблицы CLIENTS:
CREATE TRIGGER TBI_CLIENTS FOR CLIENTS
ACTIVE BEFORE INSERT POSITION 0
AS BEGIN
NEW.CLIENT_ID = GEN_ID(NEWCLIENT, 1);
END
В результате при создании новой записи полю CLIENT_ID будет автоматически присваиваться новое значение.
Однако при использовании генератора в триггере возникает проблема на клиентской стороне (например в BDE, используемом в Delphi, C++Builder ...), когда клиентское приложение пытается перечитать только что вставленную запись. Поскольку триггер меняет значение первичного ключа вставляемой записи, BDE «теряет» такую запись и чаще всего выдает сообщение «Record/Key deleted». Поскольку SQL-сервер не может сообщить клиентскому приложению о новом значении ключевого поля, необходимо сначала запросить уникальное значение с сервера, и только затем использовать его во вставляемой записи. Сделать это можно при помощи хранимой процедуры:
CREATE PROCEDURE GETNEWCLIENT
RETURNS (NID INTEGER)
AS BEGIN
NID = GEN_ID(NEWCLIENT, 1);
END
В Delphi, вы можете поместить компонент TStoredProc на форму, подсоединить его к данной процедуре, и например в методе таблицы BeforePost написать следующее
begin
if DataSource.State = dsInsert then
begin
StoredProc1.ExecProc;
ClientTable.FieldByName('CLIENT_ID').asInteger:=
StoredProc1.Params[0].asInteger;
end;
end;
После этого вышеприведенный триггер TBI_CLIENTS можно либо удалить, либо переписать так, чтобы генератор использовался только когда поле первичного ключа случайно приобрело значение NULL (например когда к таблице CLIENTS доступ осуществляется не через ваше приложение):
ALTER TRIGGER TBI_CLIENTS
AS
BEGIN
IF (NEW.CLIENT_ID IS NULL) THEN
NEW.CLIENT_ID = GEN_ID(NEWCLIENT, 1);
END
Однако использование хранимой процедуры не всегда удобно – BDE может решить, что процедура вероятно изменяет какие-то данные на сервере, и в режиме autocommit завершит текущую транзакцию, что вызовет перечитывание данных TTable и TQuery. Более простым способом является получение значения генератора при помощи запроса:
SELECT GEN_ID(NEWCLIENT, 1) FROM RDB$DATABASE;
При этом, если запрос помещен например в Query2, текст в BeforePost будет следующим:
begin if DataSource.State = dsInsert then
begin
Query2.Open;
ClientTable.FieldByName('CLIENT_ID').asInteger:=
Query2.Fields[0].asInteger;
Query2.Close;
end;
end;
Такой способ более предпочтителен, чем использование хранимой процедуры для получения значения генератора, особенно при большом количестве генераторов.
Изменение значения генератора.
Значение генератора можно переустановить при помощи оператора DDL
SET GENERATOR generatorname TO value;
Однако вы не сможете использовать такое выражение в теле триггера или хранимой процедуры, т. к. там можно использовать только операторы DML (а не DDL).
Если вы хотите обнулить генератор, или присвоить ему определенное значение в теле хранимой процедуры, то вы можете это сделать, используя функцию GEN_ID: (В данном примере генератор NEWCLIENT увеличивается на свое же значение с отрицательным знаком.)
... TEMPVAR = GEN_ID (NEWCLIENT, -GEN_ID (NEWCLIENT, 0);
...
Будьте внимательны при выполнении таких операций в многопользовательских средах. Приложения, процедуры и триггеры, которые в данный момент используют этот генератор, могут предполагать, что он не будет «обнулен». Обязательно проверяйте «обнуление» генератора на возникновение конфликтных ситуаций при работе 2-х и более пользователей.
Получение текущего значения генераторов.
Текущее значение генератора можно получить, вызвав функцию GEN_ID с нулевым увеличением значения генератора. Это можно сделать не только в триггере или хранимой процедуре, но и оператором SELECT
SELECT GEN_ID(NEWCLIENT, 0) FROM RDB$DATABASE;
Результатом выполнения запроса будет одна запись с одним полем, содержащим текущее значение генератора. Таблица RDB$DATABASES выбрана как содержащая в большинстве случаев одну запись, хотя использовать можно и любую другую таблицу.
При работе в многопользовательских средах будьте внимательны – в то время как вы получили «текущее» значение генератора, другое приложение может его изменить, и таким образом «текущее» значение окажется устаревшим. Тем более не рекомендуется использовать «текущее» значение генератора для его последующего изменения.
Удаление генераторов.
В языке DDL Borland Interbase нет оператора для удаления генератора. Неизвестно, чем это вызвано, но серьезной проблемы не представляет. В самом начале статьи было упомянуто, что запись о генераторе создается в таблице RDB$GENERATORS. Эту запись, безусловно, можно удалить. Однако место, распределенное на странице генераторов, освобождено не будет. Оно будет освобождено только после того, как вы сделаете вашей БД BACKUP/RESTORE.
Нестандартное применение генераторов.
Вы уже видели, что функцию GEN_ID можно использовать в операторе SELECT. Вот как можно получить количество записей, выбранных запросом:
SET GENERATOR MYGEN TO 0;
SELECT GEN_ID(MYGEN, 1), FIELD1, FIELD2, FIELD3, ... FROM MYTABLE;
Такой запрос вернет в качестве первого поля порядковый номер записи, и после выполнения запроса генератор MYGEN будет содержать количество возвращенных записей. Кроме этого, во время выполнения этого запроса любой другой пользователь этой же БД может получить текущее значение генератора MYGEN и узнать сколько записей уже выбрано запросом на текущий момент (нечто вроде ProgressBar, однако число записей все равно неизвестно до окончания выполнения запроса).
Функцию GEN_ID можно также использовать и как «выключатель» длительных запросов. Пример приведен для БД EMPLOYEE.GDB.
SET GENERATOR EMP_NO_GEN TO 0;
SELECT * FROM EMPLOYEE, EMPLOYEE, EMPLOYEE WHERE GEN_ID(EMP_NO_GEN, 0) = 0;
Фактически такой запрос означает – «выбирать записи пока значение генератора = 0». Как только другой пользователь или ваше приложение в другом коннекте выполнит операцию
SET GENERATOR EMP_NO_GEN TO 1;
запрос прекратится, т. к. условие WHERE станет равным FALSE.
Примечание: обязательно учитывайте буферизацию записей клиентской частью (gds32.dll) или сервером при выполнении подобных запросов. Например, приведенный выше запрос с проверкой генератора в where «выключится» не сразу, а через некоторое время.
Безусловно, в многопользовательской среде невозможно использовать в таких целях один и тот же генератор. Для решения этой проблемы можно завести глобальный генератор, который будет выдавать уникальные идентификаторы пользователям при коннекте, а клиентское приложение будет запоминать его номер и хранить на локальном компьютере для последующего использования. Логика работы может быть следующая:
Клиентское приложение при запуске определяет, есть ли для него (например в Registry или INI-файле) «именной» генератор.
Если нет, то оно операцией SELECT GEN_ID(GlobalGen, 1) FROM RDB$DATABASE получает идентификатор (например 150), создает на сервере собственный генератор операцией CREATE GENERATOR USER_N; (например USER150). После чего сохраняет имя этого генератора на локальном диске.
Если да, то приложение обнуляет «именной» генератор операцией SET GENERATOR ... TO 0; (в нашем примере SET GENERATOR USER150 TO 0;), и выдает запросы с использованием данного генератора.
При помощи генераторов можно также решить проблему с отсутствием временных таблиц в Borland Interbase. Вы создаете таблицу, например TEMP_TBL, и в качестве первого поля, входящего в первичный ключ, указываете поле типа INTEGER. Пользователь при соединении с сервером получает собственный идентификатор у некоторого общего генератора, и затем использует его при помещении записей в такую «временную» таблицу. В результате, даже если несколько пользователей будут работать с такой таблицей, они никогда не «пересекутся» по значению первого поля первичного ключа «временной» таблицы.
