Рубрика: DDL и объекты схемы

  • DDL и объекты схемы — Итоги

    Объекты БД

    • Некоторые объекты содержат данные, главные это таблицы и индексы.
    • Программные объекты такие как хранимые процедуры и функции это исполняемый код
    • Представления и синонимы это объекты которые предоставляют доступ к другим объектам
    • Талблица это двумерная структура данных, хранящая строки с определёнными столбцами
    • Таблица существует в схеме. Имя схема с именем таблицы организуют уникальный идентификатор

     

    Типы данных

    • Самые часто используемые типы данных это VARCHAR2, NUMBER и DATE
    • Существует много других типов данных

     

    Создание простой таблицы

    • Таблицы могут создаваться из ничего или используя подзапрос
    • После создания столбцы могут добавляться, изменяться и удаляться
    • Определение столбца может включать значение по умолчанию

     

    Создание и использование временных таблиц

    • Строки во временной таблице доступны только для сессии которая их добавила
    • DML надо временными таблицами не генерируют данных для отмены изменений
    • Временные таблицы существуют только в PGA сессии или во временных сегментах
    • Временная таблица может хранить данные всё время жизни сессии или только время действия транзакции, в зависимости от того как она была создана

     

    Ограничения

    • Ограничения могут определяться в момент создания таблицы и добавляться позже
    • Ограничения могут определяться в строке определения столбца или после определения столбцов
    • Ограничения табличного-уровня могут быть более гибко настраиваемы чем ограничения создаваемые в строке определения столбца
    • Таблица может иметь только один первичный ключ но много ограничений уникальности
    • Превичные ключ функционально равен ограничению уникальности вместе с ограничением обязательности
    • Ограничение уникальности не предотвращает от возможности вставки нескольки строк со значением NULL
    • Внешний ключ определяет отношения между таблицами

     

    Индексы

    • Индексы необходимы для работы ограничений уникальности и первичных ключей
    • Значени NULL не влючается в B* Tree индекс, но включается в bitmap индекс
    • B* Tree индексы подходят для столбцов с высокой мощностьб, битмап индексы – для столбцов с маленькой мощностью
    • B* Tree индекс может быть уникальным или неуникальным, что определяет возможность хранить дубликаты в значении ключа
    • Bitmap индексы могут быть составными, основанными на функции, и сортированными; B* Tree индексы в добавок к этим свойствам так же ещё могут быть уникальными, сжатыми и обратными

     

    Представления

    • Простые представления используют одну (базовую или детальную) таблицу и не используют ни функции ни аггрегацию
    • Сложные представления основаны на команде SELECT вне зависимости от сложности запроса
    • Представления это объекты схемы. Для использования представления из другой схемы, имя представления должно предваряться именем схемы
    • К представлениям можно писать запросы как будто это таблицы
    • Предстваления можно объединять с другими представлениями или таблицами, их можно аггрегивароть и в некоторых случаях использовать для DML команд
    • Представления существуют только как конструкции в словаре данных. Когда вы выполняете запрос к представлению запрос-основа всегда выполняется

     

    Синонимы

    • Синонимы это псевдоним для представления или таблицы
    • Частные синонимы это объекты схемы; общие синонимы существуют вне пользовательской схемы и могут использоваться без указания схемы в уникальном идентификаторе
    • Синонимы используют тоже пространство имен что и представления и таблицы

     

    Сиквенсы

    • Сиквенсы генерируют уникльные значения – до тех пор пока MAXVALUE или MINVALUE и CYCLE не указаны
    • Увеличение значения сиквенса не может подтверждаться или отменяться
    • Любая сессия может увеличить значение сиквенса путём запроса следующего значения. Также возможно получить текущее (последнее использованное) значение для текущей сессии но не последнее сгенерированное значение.
  • Сиквенсы (Sequences)

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

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

    Мехнизм сиквенсов не зависит от таблиц, механизма блокировок и транзакций. Это значит что сиквенс может генерировать тысячи уникальных значений в минуту – гораздо быстрее чем методы выборки данных, обновления и подтверждения изменений.

    На рисунке 7-6 показано как две сессий выбирают значения из сиквенса SEQ1. Обратите внимание что каждый запрос SEQ1.NEXTVAL генерирует уникальный номер. Значение создаётся по порядку в зависимости от времени обращения, и значение увеличивается глобально а не для одной сессии.

    86

    Создание сиквенсов

    Полный синтаксис для создания сиквенса

     

    CREATE SEQUENCE [schema.]sequencename

    [INCREMENT BY number]

    [START WITH number]

    [MAXVALUE number | NOMAXVALUE]

    [MINVALUE number | NOMINVALUE]

    [CYCLE | NOCYCLE]

    [CACHE number | NOCACHE]

    [ORDER | NOORDER] ;

     

    Создание сиквенса может быть очень простым. Например сиквенс использованный на рисунке 7-6 был создан с помощью команды

     

    create sequence seq1;

     

    Список доступных параметров

    87

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

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

     

    TIP

    The default number of values to cache is only 20. Experience shows that this is usually not enough. If your application selects from the sequence 10 times a second, then set the cache value to 50 thousand. Don’t be shy about this

    Использование сиквенсов

    Для использования сиквенса сессия может запросить следующее значения используя псевдо-столбец NEXTVAL, который заставляет сиквенс увеличить значение, или запросить последнее (текущее) значение для текущей сессии используя псевдостолбец CURRVAL. Значение NEXTVAL будет глобально уникальным: каждая сессия которая запрашивает это значение будет получать разный, увеличенный результат для каждого запроса. Значение CURRVAL будет постоянным для каждой сессии пока не будет новый запрос к NEXTVAL. Нет возможности узнать какое последнее значение было сегенрировано сиквенсом: вы можете выбрать только следующее значение вызвав NEXTVAL, и узнать последнее использованное значение для вашей сессии используя CURRVAL. Но вы не можете узнать последнее сгенерированное значение.

    EXAM TIP

    The CURRVAL of a sequence is the last value issued to the current session, not necessarily the last value issued. You cannot select the CURRVAL until after selecting the NEXTVAL.

     

    Типичным примером использования сиквенса является генерация значений первичного ключа. Следующий пример использует сиквенс ORDER_SEQ для генерации уникальных значений номера заказа и сиквенс LINE_SEQ для генерации уникального значения строки заказа. Вначале создаётся сиквенс (один раз)

     

    create sequence order_seq start with 10;

    create sequence line_seq start with 10;

     

    Затем вставка заказа и пунктов заказа в одной транзакции

     

    insert into orders (order_id,order_date,customer_id)

    values (order_seq.nextval,sysdate,’1000′);

    insert into order_items (order_id,order_item_id,product_id)

    values (order_seq.currval,line_seq.nextval,’A111′);

    insert into order_items (order_id,order_item_id,product_id)

    values (order_seq.currval,line_seq.nextval,’B111′);

    commit;

     

    Первая команда INSERT создает заказ с уникальным номером из сиквенса ORDER_SEQ для покупателя с номером 1000. Затем вторая и третья команды INSERT добавляют два элемента заказа используя текущее значение сиквенса ORDER_SEQ как значение для внешнего ключа соединяющего элементы заказа с заказом и следующее значение сиквенса LINE_SEQ для генерации уникального идентификатора каждого элемента. И наконец транзакция подтверждается.

    Сиквенс не привязан к какой-то таблице. В предыдущем примере можно использовать один сиквенс для генерации значений для первичны ключей таблицы заказов и таблицы элементов заказа.

    COMMIT не обязателен для подвтерждения увеличения счетчика: увеличение счётчика происходи сразу и навсегда и становится видимым для всех в момент увеличения. Нельзя отменить увеличение счётчика. Сиквенс обновляется вне зависимости от механизма управления транзакциями. Поэтому всегда будут пропавшие номера. Разрывы могут быть большими если БД перезапускается и CACHE директива использовалась для счётчика. Все номера которые были сгенерированы и не выбирались будут потеряны в момент выключения базы данных. После следующего запуска текущее значение будет последнее сгенерированное, а не последнее использованное. Таким образом для значения по умолчанию 20, каждый перезапуск приводит к потере 20 номеров.

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

     

    create table current_on(order_number number);

    insert into current_on values(10);

    commit;

     

    Тогда код для создания заказа станет следующим

     

    update current_on set order_number=order_number + 1;

    insert into orders (order_number,order_date,customer_number)

    values ((select order_number from current_on),sysdate,’1000′);

    commit;

     

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

    После создания сиквенса он может быть изменена. Синтаксис команды следующий

     

    ALTER SEQUENCE sequencename

    [INCREMENT BY number]

    [START WITH number]

    [MAXVALUE number | NOMAXVALUE]

    [MINVALUE number | NOMINVALUE]

    [CYCLE | NOCYCLE]

    [CACHE number | NOCACHE]

    [ORDER | NOORDER] ;

     

    Команда ALTER такая же как команда CREATE за одним исключением: нельзя установить начальное значение. Если вы хотите обновить начальное значение – то единственный способ это удалить сиквенс и создать новый. Для изменения значения CACHE для увеличения производительности можно выполнить следующую команду

     

    alter sequence order_seq cache 1000;

     

    Для удаления сиквенса выполните команду

     

    drop sequence order_seq;

  • Синонимы

    Синоним – это альтернативное имя объекта. Если создан синоним для объекта, то все SQL команды к объекту могут выполняться используя настоящее имя или синоним. Это может показаться очень простым, но это не так. Использование синонимов позволяет приложениям работать для любого пользователя, вне зависимости от схемы в которой лежат таблицы и представления и даже БД. Рассмотрим запрос

     

    select * from hr.employees@prod;

     

    Пользователь выполняющий такой запрос должен знать что таблица employees принадлежит схеме HR в БД определяемой сслыкой PROD. Если общий синоним был созда таким запросом

     

    create public synonym emp for hr.employees@prod;

     

    то все пользователи (все!) могут использовать запрос вида

     

    select * from emp;

     

    Это предоставляет независимость данных и абстракцию местонахождения. Таблицы и представления могут быть переименованы или перемещены без необходимости изменения кода; необходимо только поправить синоним.

    Так же как и команды SELECT, DML команды могут обращаться к синонимам так же как к объектам на которые они ссылаются.

    Приватные синонимы (private synonyms) – это объекты схемы. Они могут быть или в текущей схеме, или использоваться вместе с именем схемы. Общие (public) синонимы существуют независимо от схемы. Общие синонимы могут использоваться любым пользователем кому назначены соответсвующие права без использования имени схемы. Приватные синонимы должны иметь уникальное имя в схеме. Общие синонимы могут иметь такое же имя как и иям объекта схемы. Когда выполняется запрос без указания схемы, Oracle вначале проверяет есть ли объект с таким именем в текущей схеме, и только если он не найдет Oracle попробует найти общий синоним с таким именем. То есть если в предыдущем примере у пользователя есть таблица EMP – это будет то что отобразится пользователю – не таблица назначенная общему синониму.

    Синтаксис создания синонима

     

    CREATE [PUBLIC] SYNONYM synonym FOR object ;

     

    Пользователю должны быть назначены права для создания синонимов (приватных) и дополнительные права для создания общих синонимов. Обычно только администратор БД может создавать и удалять общие синонимы. Это правильно потому что их создание (или удаление) влияет на всех пользователей.

     

    EXAM TIP The “public” in “public synonym” means that it is not a schema object and cannot therefore be prefixed with a schema name. It does not mean that everyone has permissions against it.

     

     

     

    Для удаления синонима используется команда

     

    DROP [PUBLIC] SYNONYM synonym ;

     

    Если объект на который ссылается синоним (таблица или представление) удалён, синоним не удаляется. Любое обращение к синониму вернёт ошибку. В этом отношении синоинмы работают как представления. Если объекты были вновь созданы, синонимы должны быть перекомпилированы перед использованием. Как и для представлений, это случится автоматически при обращении к синониму или вручную использовав команду

     

    ALTER SYNONYM synonym COMPILE;

  • Представления

    Для пользователя представления выглядят как таблицы: двумерная структура из строк и столбцов, к которой пользователь может писать SELECT и DML запросы. Программист знает правду: представление это всего лишь команда SELECT с именем. Любой SELECT запрос возвращает двумерный набор данных. Когда SELECT запрос сохранен как представление, то когда бы пользователь не читал или обновлял данные в представлении (думая что это таблица) запрос выполняется и результат представляется пользователя как будет это таблица. Команда SELECT на которой базируется представление может быть чем угодно. Объединением таблиц, результатом после аггрегации, сортировки. Абсолютно любой валидный запрос может быть основой для представления.

     

    EXAM TIP

    Views share the same namespace as tables: anywhere that a table name can be used, a view name is also syntactically correct.

    Зачем нужны представления

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

    Представления для безопасности

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

    create view hr.emp_fin as select hire_date,job_id,salary,commission_pct,department_id from hr.employees;

     

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

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

     

    select * from emp_fin where department_id=50;

     

    Будет доступно всего пять столбцов вместо всех столбцов таблицы EMPLOYEES. Представление можно объединять с другими таблицами или использовать функции аггрегация как будто это таблица

     

    select department_name, sum(salary) from departments natural join emp_fin group by department_name;

     

    Хорошо продуманный набор представлений может реализовывать всю систему безопасности в БД, предоставляя пользователям доступ к данным которые им необходимы и сокрывая всё остальное.

    Представления для упрощения запросов

    Для пользователя будет гораздо проще выбирать данные если сложная работа (такая как объединения таблиц или группировка данных с аггрегацией) сделано за них в коде запроса который определяет представление. В предыдущем примере сотрудникам финансового отдела необходимо было объединять представление EMP_FIN с таблицей DEPARTMENTS и суммировать зарплату по департаменту. Но можно создать новое представление

     

    create view dept_sal as select d.department_name, sum(e.salary) from departments d left outer join employees e on d.department_id=e.department_id group by department_name order by department_name;

     

    И тогда сотрудники смогут писать запросы к представлению DEPT_SAL без необходимости знать ою объединениях или о том как сортировать результат

     

    select * from dept_sal;

     

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

    Представления для предотвращения ошибок

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

    Представление помогает представить данные в недвусмысленном виде. Например многие приложения в реальности не удаляют данные. Рассмотрим таблицу

     

    create table emp(empno number constraint emp_empno_pk primary key,ename varchar2(10),deptno number,active varchar2(1) default ‘Y’);

     

    Столбец ACTIVE это флаг показывающий нанят ли сотрудник в текущий момент и при добавлении строки будет выставлен в ‘Y’. Когда пользователь, через пользовательский интерфейс “удалит” сотрудника, на самом деле выполнится запрос который обновит значение ACTIVE в ‘N’. Если пользователь не знает о структуре таблицы и такой особенности то результат “удаления” будет не очень понятен. Поэтому лучше дать пользователю доступ к представлению

     

    create view current_staff as select * from emp where active=’Y’;

     

    Запросы к такому представлению не отображат “удалённых” сотрудников.

    Представления чтобы сделать данные понятными

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

    Помимо удобства предоставления данных пользователю, использование представлений добавляет уровень абстракции между объектами пользователя и объектами БД что может быть полезно при разработке и поддержке. Возможно изменить структуру данных без переписывания приложения. Если изменились таблицы достаточно просто изменить определение таблицы без изменений кода SQL или PL/SQL. Также представления можно использовать для добавления приложению совместимости между разными базами данных.

    Представления для производительности

    Команда SELECT являющаяся основой для представления может быть оптимизирована программистами, и пользователям не надо переживать об оптимизации кода. Существует много способов получения одного и того же результата, но некоторые способы гораздо медленнее чем другие. Например при объединении двух таблиц обычно происходи выбор между nested loop объединением и hash join объединением. Nested loop использует индекса для поиска конкретной строки, hash join считывает всю таблицу в память. Выбор между методами основывается на данных и необходимых ресурсах.

    Теоретически кто-то всегда полагается на результат работы оптимизатора Oracle, но иногда оптимизатор совершает ошибки. Если программист понимает какой метод лучше использовать в конкретном случае то можно дать необходимые иснтрукции оптимизатору. Например этот запрос заставит использовать hash join

     

    create view dept_emp as select /*+USE_HASH (employees departments)*/ department_name, last_name from departments natural join employees;

     

    Когда бы пользователь не выполнил запрос к представлению DEPT_EMP объединение будет осуществляться поиском совпадений в подсоединяемой таблице в памяти (hash join). Пользователям не нужно знать как заставить базу использовать метод объединения. Мы тоже не будем детально обсуждать оптимизацию но необходимо знать принцип оптимизации при помощи представлений.

    Простые и сложные представления

    С практической точки зрения, классификация между простыми и сложными представлениями основана на возможности выполнять DML команды к представлению: простые представления могут (обычно) выполнять DML запросы; сложные – не могут. Определим какие представления простые, а какие сложные

    • Простые представления используют данные из одной таблицы, без использваония функций и аггрегации
    • Сложные представления могут использовать объединениям, функции и аггрегацию.

    Согласно этим определениями первое и третье представления из прошлой подглавы являютяс простыми, а второе и четвертое сложные.

    Команды INSERT, UPDATE  или DELETE нельзя выполнить к сложным представлениям. Соотношение строк в представлении к исходной таблице неможет всегда быть один-к-одному, что необходимо для DML операций. Обычно возможно выполнять DML команды к простым представлениям, но не всегда. Например если представление не включает в себя столбец с ограничением обязательности, тогда INSERT к представление не выполнится успешно (выполнится если у столбца есть значение по умолчанию). Выполнение такого запроса вернет странную ошибку так как ошибка ссылается на таблицу и столбец которого нет в запросе, как показано на примере в рисунке 7-5.

    85

    Превое представление RNAME_V на рисунке удовлетворяет определению простого представления, однаго команда INSERT не может быть выполена так как отсутствует значение для обязательного поля. Второе представление RUPPERNAME_V – сложное представление так как основано на результате выполнения функции. Это делает невозможным вставку значений, так как нет способа БД узнать что действительно необходимо вставить в таблицу. Однако команда DELETE может быть выполнена, так как нет зависимости от использования функции.

    Создание, изменение и удаление представлений

    Синтаксис создания представления

     

    CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW

    [schema.]viewname [(alias [,alias]…]

    AS subquery

    [WITH CHECK OPTION [CONSTRAINT constraintname]]

    [WITH READ ONLY [CONSTRAINT constraintname]] ;

     

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

    OR REPLACE – если представление уже существует оно будет удалено перед созданием нового

    FORCE или NOFORCE – Использование FORCE приведёт к созданию представления даже если базовые таблицы не существуют. NOFORCE значение по умолчанию и если таблицы не существуют команды выполняется с ошибкой

    WITH CHECK OPTION – эта директива влияет на DML команды. Если подзапрос включает условие WHERE, тогда эта директива предотвратит возможность вставки стро которые не видно в представлении, или совершать обновления данных которое приведёт к пропаже данных из представления. По умолчанию эта директива отключена что может приводить к неожидаемым результатам выполнения запросов

    WITH READ ONLY – отключения возможности использование DML команд к представлению

    CONSTRAINT constraintname – позволяет назначить имя ограничениям WITH CHECK OPTION и WITH READ ONLY и сообщения об ошибке станут более понятными

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

    Команда ALTER VIEW в основном используется для компиляции представления. Представление должно быть успешно скомпилированно перед использованием. Когда преставление создаётся, Oracle проверяет что все столбцы и таблицы существуют. Если они не существуют компиляция происходит неудачно и представление не будет создано – но будет создано если вы используете директиву FORCE. В этом случае представление создастся, но будет недоступно для использования пока не будут созданы таблицы и столбцы используемые в подзапросе и не будет заново скомпилировано. Когда выполняется запрос к нескомпилированному представлению – Oracle попробует скомпилировать его автоматически. Если компиляция будет успешна (вы устранили проблемы) – то пользователи даже не узнает что что-то не работало – единственное отличие будет в том что запрос будет выполняться чуть дольше. Вам следует самим вручную компилировать представления чтобы убедиться что компиляция прошла успешно, вместо того чтобы позволять пользователям обнаружить ошибку.

    Невозможно изменить определения столбцов после создания представления так как это делается для таблицы. Представление должно быть удалено и затем создано новое. Синтаксис команды DROP

     

    DROP VIEW [schema.]viewname ;

    Использование директивы OR REPLACE  в команде CREATE VIEW приведёт к автоматическому удаление представления (если оно существует) перед созданием.

  • Ограничения

    Ограничения являются средством, с помощью которого БД может принуждать к выполнению бизнес-правил и гарантировать что данные соответсвуют модели сущность-связь определённой  системным анализом определяя структуры данных приложения. Например, бизнес-аналитик в компании решил что каждый покупатель и каждый заказ должен определяться уникальным числом, что нельзя создать заказ до создания покупателя и каждый заказ должен иметь валидную дату и сумму большую нуля. Это может быть достигнуто путём создания первичных ключей для столбца CUSTOMER_ID таблицы CUSTOMERS и столбца ORDER_ID таблицы ORDERS, создания внешнего ключа для таблица ORDERS ссылающегося на таблицу CUSTOMERS, создания ограничения “not null” для столбца ORDER_DATE (тип данных DATE сам проверит удовлятворяет ли введённое значение типа дата или нет) и ограничения проверки для столбца ORDER_AMOUNT таблицы ORDERS.

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

     

    EXAM TIP

    A constraint violation will force an automatic rollback of the entire statement that hit the problem, not just the single action within the statement, and not the entire transaction.

    Типы ограничений

    Ограничения поддерживаемые Orace это

    • UNIQUE
    • NOT NULL
    • PRIMARY KEY
    • FOREIGN KEY
    • CHECK

    У ограничений есть имя. Хорошей практикой считается именовать их согласно конвенции имен, но если явно не указывать имя, Oracle будет использовать сгенерированное имя.

    Ограничение уникальности

    Ограничение уникальности назначает столбец (или группу столбцов) для которых значение должно быть разным для каждой строки в таблице. Если ограничение настроено для одного столбца, это называется столбец-ключ (key column). Если ограничение состоит из нескольких столбцов (называется составной ключ (composite key)), столбцы не должны быть одинакового типы и располагаться последовательно в таблице.

    Странностью уникального ограничения можно считать возможность добавления значения NULL в столбцы ключа; более того можно иметь неограниченное количество строк со значение NULL в столбцах ключа. Таким образом запрос строк по ключу ограничения гарантирует одну строку для значения – пока не используется NULL, в таком случае все строки со значением NULL будут результатом запроса.

     

    EXAM TIP

    It is possible to insert many rows with NULLs in a column with a unique constraint. This is not possible for a column with a primary key constraint.

     

    Ограничения уникальности работают с помощью индексов. Когда мы определяем ограничение, Oracle проверит существует ли индекс для столбцов ограничения, и если индекс не сущетвует, он будет создан. Затем когда происходи попытка вставки новой строки, Oracle просматривает индекс на существование значения ключа; если значение сущесвтует – строка будет отклонена. Индекс ограничения – B* Tree индекс, и он не хранит значения NULL, вот почему ограничение не работает для NULL (т.е. можно добавлять много строк со значением NULL). Как мы обсуждали выше, целью индексов является не только обеспечение работоспособности механизма ограничений, но и повышение производительности запросов с секцией WHERE и объединением таблиц. Если в запросе используется WHERE key_column IS NULL – Oracle не может использовать индекс (потому что индекс не хранит значения NULL) и всегда будет использовано полное чтение таблицы.

    Ограничения NOT-NULL

    Ограничения NOT-NULL принуждают указать данные для столбцов ключа. Эти ограничения накладываются на столбец и иногда их называют обязательными для заполнения столбцами (mandatory columns). Если несколько столбцов должны иметь значения, то вы не можете создать одно ограничение для группы – необходимо создавать ограничения для каждого столбца.

    Любая попытка записи строки без указания значения для обязательного столбца вернёт ошибку. Возможно обойти эту проверку указав директиву DEFAULT при создании таблицы.

    Первичный ключ

    Первичный ключ является средством для поиска любой одной строки в таблице. Реляционная парадигма утверждает что каждая таблица должна иметь первичный ключ: столбец (или группу столбцов) которые можно использовать для выделения каждой строки. Oracle позволяет создавать таблицы (как и некоторые другие РСУБД) без первичного ключа.

    Реализацией первичного ключа является комбинация ограничений целостности и обязательности. Значение в столбцах ключе должно быть уникальным и не NULL. Так же как и для ограничения уникальности индекс должен существовать для столбцов ключа, иначе он будет создан. У таблицы может быть только один первичный ключ. Но у таблицы может быть сколько угодно ограничений уникальности и not-null ограничений. Если у вас есть несколько столбцов которые должны быть уникальны и обязательны вы можете использовать часть из них для создания первичного ключа, а для остальных создать ограничения уникальности и обязательности. Например таблица сотрудников: все сотрудники должны иметь уникальный номер (первичный ключ) и также значения email, номер соц. страха должны быть уникальны и обязательны для заполнения.

     

    EXAM TIP

    Unique and primary key constraints need an index. If one does not exist, one will be created automatically.

    Внешние ключи

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

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

     

    EXAM TIP

    A foreign key constraint is defined on the child table, but a unique or primary key constraint must already exist on the parent table.

     

    Как и ограничение уникальности внешний ключ допускает значение NULL в столбцах ключа. Вы можете вставить строки в дочернюю таблицу со значением NULL в столбцах внешнего ключа – даже если в родительской таблице нет строки со значением NULL. Это создаст строки призраки (orphaned rows) и может вызвать определённые недоразмения. Как правило все столбцы ограничения уникальности и все столбцы внешнего ключа лучше определять вместе с ограничениями not-null: так же это обычно требование бизнеса.

    При попытки вставить строку в дочернюю таблицу со значением ключа которого нет в родительской таблице – произойдёт ошибка выполнения запроса. Также попытка удаления строки в родительской таблице вернёт ошибку если существуют строки в дочерней которые ссылаются на эту строке. Существует два способа обойти это ограничение. Во-первых, ограничения могут быть созданы с директивой ON DELETE CASCADE. Это значит что если мы удаляется строка из родительской таблицы – Oracle автоматически найдёт все строки ссылающиеся на эту строку в дочерней таблице и удалит их тоже. Менее кардинальный способ – использование директивы ON DELETE SET NULL. В этом случае если строка родительской таблицы удаляется – Oracle найдёт все соответствующие строки в дочерней таблице и установит значение ключа в NULL. Это значит что строки в дочерней таблице станут строками-призраками – но они будут существовать в таблице. Если столбцы внешнего ключа имеют ограничение not-null – то удаление из родительской таблицы вернёт ошибку. Нельзя ни удалить ни выполнить операцию TRUNCATE для родительской таблицы, даже если нет ни одной строки в дочерней таблице. Даже если использованы ON DELETE SET NULL или ON DELETE CASCADE.

    Отдельным видом внешнего ключа является внешний ключ ссылающийся сам на себя (self-referencing). Такой ключ определяет отношение где родительский и дочернии строки находятся в одной таблице. Хорошим примером является таблица сотрудников, в которой есть столбец MANAGER_ID. И менеджер и сотрудник являются сотрудниками. Если первичный ключ EMPLOYEE_ID то для внешнего ключа используем EMPLOYEE_ID как родительский столбец и MANAGER_ID как дочерний. Если у сотрудника нет менеджера, то такая строка указывает сама на себя.

    Ограничения проверки значения (Check constraints)

    Ограничения проверки значения используются для проверки данных на удовлетворение простым правилам, к примеру введённое значение должно попадать в период значений. Правилом должно быть выражение результатом проверки которого будет либо ПРАВДА либо ЛОЖЬ. Правила могут использовать литералы, или другие столбцы той же строки и они могут использовать некоторые функции. Сколько угодно правил можно применить к одному ограничение проверки значения, но нельзя использовать подзапросы или такие функции как SYSDATE.

     

    TIP

    The not-null constraint is in fact implemented as a preconfigured check constraint.

     Определение ограничений

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

    Расмотрим два запроса создания таблицы

     

    1 create table dept(

    2 deptno number(2,0) constraint dept_deptno_pk primary key

    3 constraint dept_deptno_ck check (deptno between 10 and 90),

    4 dname varchar2(20) constraint dept_dname_nn not null);

    5 create table emp(

    6 empno number(4,0) constraint emp_empno_pk primary key,

    7 ename varchar2(20) constraint emp_ename_nn not null,

    8 mgr number (4,0) constraint emp_mgr_fk references emp (empno),

    9 dob date,

    10 hiredate date,

    11 deptno number(2,0) constraint emp_deptno_fk references dept(deptno)

    12 on delete set null,

    13 email varchar2(30) constraint emp_email_uk unique,

    14 constraint emp_hiredate_ck check (hiredate >= dob + 365*16),

    15 constraint emp_email_ck

    16 check ((instr(email,’@’) > 0) and (instr(email,’.’) > 0)));

     

    • Первая таблица создаётся с именем DEPT и предназначена для хранения одной строки для каждого департамента.
    • DEPTNO – числовое поле, 2 цифры, нет точек после запятой. Первичный ключ таблицы – имя ограничения DEPT_DEPTNO_PK.
    • Второе ограничение столбца DEPTNO это проверка значения на период от 10 до 90. Имя этого ограничения DEPT_DEPTNO_CK.
    • Столбец DNAME – это строка нефиксированной длины, обязательна для заполнения – ограничение DEPT_DNAME_NN.
    • Вторая табилца это EMP, предназаченная для хранения строки для каждого сотрудника.
    • Столбец EMPNO типа данных NUMERIC, 4 цифры без дробной части первичный ключ EMP_EMPNO_PK. ENAME
    • Столбец ENAME строка произвольной длины до 20 символов с not null ограничением EMP_ENAME_NN
    • Столбец MGR определён так же как и EMPNO плюс является внешним ключом таблицы к самой себе по полям MGR-EMP_NO. Нет ограничения на not-null поэтому может быть NULL
    • DOB – дата рождения сотрудника, тип данных дата нет ограничений
    • HIREDATE – дата приёма на работу, нет ограничений
    • DEPTNO департамент, определение столбца такое же как столбец DEPTNO таблицы DEPT. Внешний ключ к таблице департаментов: нельзя вставить строку со значением которого нет в таблице DEPT. Но значение может быть
    • Внешний ключ EMP_DEPTO_FK определён с директивой ON DELETE SET NULL, т.е. при удалении строки из таблицы DEPT значение столбца DEPTNO во всех относящиеся к удаляемой строке будет обновлено в значение NULL.
    • EMAIL – строка переменной длины и имеет ограничение уникальности (но может быть NULL)
    • Определение дополнительного ограничения EMP_HIREDATE_CK. Дата приёма на работу должна быть не раньше чем 16 лет после даты рождения. Данное ограничение невозможно создать в строке определения столбца HIREDATE, так как ограничение ссылается на другой столбец
    • Определение дополнительного ограничения проверки значения для строки EMAIL. Вызываются функции которые проверяют наличие @ и “.” в значении и если хотя бы одна функция вернёт ЛОЖЬ то строка будет отклонена.

     

    Эти примеры показывают некоторые возможности определения ограничения при создании таблицы. Остальные возможности это

    Управление созданием индексов для внешних ключей и ограничений уникальности

    Определение времени проверки ограничения: во время вставки (значение по умолчанию) или позже, в момент подтверждения транзакции

    Включено ли ограничение (по умолчанию) или отключено

    Возможно создать таблицу без ограничений а затем добавить ограничения командой ALTER TABLE. Результат будет одинаковый но определение таблицы будет разбито на несколько запросов вместо одного.

    Состояния ограничений

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

    • ENABLEVALIDATE – Невозможно добавить строку которая нарушает ограничение и все строки таблицы удовлетворяют ограничениям
    • DISABLENOVALIDATE – Любые данные возможно ввести и в таблицу уже могут быть данные нарушающие ограничения
    • ENABLENOVALIDATE – В таблице могут существовать данные не удовлетворяющие ограничениям, но все добавляемые данные должны удовлетворять ограничениям
    • DISABLEVALIDATE – невозможная ситуация: все данные в таблице удовлетворяют ограничениям, но новые данные должны неудовлетворять. Т.е. нельзя вставить данные в таблицу.

     

    Идеальной ситуацией (и состоянием по умолчанию при определении ограничения) является ENABLE VALIDATE. Это гарантирует что данные корректны и некорректные данные не могут быть добавлены.

    Статус DISABLE NOVALIDATE полезен когда загружается много данных в таблицу одним запросом. Данные могут содержать плохие значения, но вместо ошибки выполнения запроса этот статус позволит загрузить данные. Сразу после загрузки данных, переведите состояние в ENABLE NOVALIDATE чтобы предотвратить вставку других неправильных данных пока вы будете проверять уже добавленные данные на удовлетворение ограничениям. И затем опять установите правильное состояние ENABLE VALIDATE.

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

     

    alter table sales_archive modify constraint sa_nn1 disable novalidate;

    insert into sales_archive select * from sales_current;

    alter table sales_archive modify constraint sa_nn1 enable novalidate;

    update sales_archive set channel=’NOT KNOWN’ where channel is null;

    alter table sales_archive modify constraint sa_nn1 enable validate;

    Проверка ограничений

    Ограничения могут проверятся на момент выполнения запроса (ограничение IMMEDIATE) или когда подтверждается вся транзакция (DEFERRED). По умолчанию все ограничения в режиме IMMEDIATE. Это может быть альтернативой использования состояний. Можно переписать скрипт из подпункта «Состояние ограничений» таким образом

     

    set constraint sa_nn1 deferred;

    insert into sales_archive select * from sales_current;

    update sales_archive set channel=’NOT KNOWN’ where channel is null;

    commit;

    set constraint sa_nn1 immediate;

     

    Для поддержки отложенного (DEFERRED) ограничения оно должно быть создано используя синтаксис

     

    alter table sales_archive add constraint sa_nn1

    check (channel is not null) deferrable initially immediate;

     

    То есть невозможно сделать ограничение отложенным если директива deferrable не была указана при создании. В нашем случае ограничение SA_NN1 по умолчанию проверяется на момент вставки строки (или изменения), но проверку можно отложить до подтверждения транзакции. Часто отложенные ограничения используются для внешних ключей. Если операции обновления и изменения затрагивают и дочернюю и родительскую таблицу, и если внешний ключ не отложенный то операция может выполниться с ошибкой в зависимости от порядка обработки строк.

    Изменение состояния ограничения затрагивает все сессии. Изменение времени проверки ограничения (IMMEDIATE или DEFERRED) затрагивает текущую сессию, но начальное состояние применяется ко всем сессиям.

     

    EXAM TIP

    By default, constraints are enabled and validated, and they are not deferrable.

  • Индексы

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

    Почему индексы необходимы

    Индексы это часть механизма ограничений (constraint). Если столбец (или группа столбцов) помечены как первичной ключ таблица, то каждый раз когда вставляется строка в таблицу, Oracle необходимо проверить что не существует строки с такими значениями. Если у таблицы нет индекса  дял столбцов – единственный способ проверить это это вычитать всю таблицу. Это может быть приемлимо если в таблице всего несколько строк, но дял таблиц, содержащих тысячи миллионов (или миллиардов) строк это займёт очень много времени и неприемлимо. Индекс позволяет практически мгновенно получить доступ к значениям ключа и проверка на существование происходит моментально. Когда определяется первичный ключ Oracle создаст индекс для столбца(ов) ключа если ещё не существует такого индекса.

    Ограничение по уникальности (unique constraint) тоже требует создание индекса. Это ограничение отличается от первичного ключа тем что значение в столбцах ограничения по уникальности могут быть NULL в отличие от первичного ключа, но это не влияет на создание и исопльзование индекса. Внешний ключ (foreign key) соблюдается с помощью индексов, но обязательным является индекс только на родительской таблице. Внешний ключ дочерней таблицы зависит от столбца первичного ключа или уникального ключа родительской таблицы. Когда строка добавляется в дочернюю таблицу, Oracle будет использовать индекс родительской таблицы для проверки существует ли такое значение в родительной таблице или нет, перед тем как позволить записать данные. Как бы то ни было желательно всегда создавать индексы для столбцов дочерней таблицы используемых как внешние ключи из соображений производительности: DELETE для родительской таблицы будет гораздо б ыстрее если Oracle сможет использовать индекс для проверки существуют ли ещё строки в дочерней таблице с этим значением или нет.

    Индексы критически важны для производительности. Когда выполняется команда SELECT с директивой WHERE, Oracle необходимо определить строки в таблице которые необходимо выбрать. Если не создано индексов для столбцов используемых в директиве WHERE, то единственным способом сделать это – это вычитать всю таблицу (full table scan).Full table scan проверяют все строки по очереди для поиска нужных значений. Если в таблицы хранятся миллиарды строк, это может занять несколько часов. Если существует индекс для использованного в WHERE столбца, Oracle может искать используя индекс. Индекс это отсортированный список ключей значений структурирвоанных таким образом чтобы операция поиска была очень быстрой. Каждая запись это сслыка на строку в таблице. Поиск строк используя индекс гораздо быстрее чем чтение всей таблицы если размер таблицы больше определённого размера и пропорция между данными которые нужны для запроса и всеми данными в таблице ниже определённого значения. Для маленьких таблиц, или где секция WHERE всё равно выберет большую часть строк из таблицы, полное чтение таблицы будет быстрее: вы можете (обычно) доверять Oracle при выборе решения использовать ли индекс. Это решение осуществляется на основании статистической информации собираемой о таблице и строках в ней.

    Второй случай когда индексы могут увеличить производительность это сортировка. Команда SELECT c директивой ORDER BY, GROUP BY или ключевым словом UNION (и несколько других) обязана отсортировать строки в определённом порядке – если не создан индекс, который может вернуть строки без необходимости в сортировке (строки уже отсортированы).

    И третий случай это объекдинение таблиц, но опять же у Oracle есть выбор: в зависимости от размера таблиц и наличия свободной памяти, может быть быстрее вычитать таблицы в память и объединять их чем использовать индексы. Метод nested loop join читает строки одной таблицы и использует индекс другой таблицы для поиска совпадений (это обычно нагружает диск). Hash join считывает таблицу в память, преобразует в хеш таблицу и использует специальный алгоритм для поиска совпадений  — такая операция требует больше оперативной памяти и процессорного времени. Sort merge join сортиует таблицы по значениям столбца для объединения и затем объединяет их вместе – это компромисс между использованием диска, памятии процессора. Если нет индексов –Oracle сильно ограничен в способах объединения.

     

    https://habrahabr.ru/company/mailru/blog/266811/ кому интерестно – рекомендую почитать

     

    TIP

    Indexes assist SELECT statements, and also any UPDATE, DELETE, or MERGE statements that use a WHERE clause—but they will slow down INSERT statements.

    Типы индексов

    Oracle поддерживает несколько типов индексов с различными вариациями. Два типа, которые мы рассмотрим это B* Tree индекс, который является типом по умолчанию и bitmap индекс. Основное правило – индексы увеличивают производительность для чтения данных но замедляют при DML операциях. Это происходит потому что индексы нужно обновлять и поддерживать. Каждый раз когда строка записывается в таблицу, новый ключ должен быть вставлен в каждый индекс таблицы, что усиливает нагрузку на БД. Поэтому OLTP системы обычно используют минимальное количество индексов (возможно только необходимые для ограничений) а для OLAP систем создаётся столько индексов сколько нужно для быстроты выполнения.

    B* Tree индексы (B*=balanced)

    Индекс это древовидная (tree) структура. «Корень» (root) дерева содержит указатели на множество узлов второго уровня, которые в свою очередь могут хранить указатели на узлы третьего уровня и так далее. Глубина дерева определяется длинной ключа и количеством строк в таблице.

     

    TIP

    The B*Tree structure is very efficient. If the depth is greater than three or four, then either the index keys are very long or the table has billions of rows. If neither if these is the case, then the index is in need of a rebuild.
    78

    В листьях (узлы нижнего уровня) индекса хранятся значения столбца строк по порядку и указатель на строку. Также листья хранят ссылки на соседние листья. Таким образом чтобы выбрать строку если условие WHERE использует строгое равенство — Oracle исдёт по дереву в лист содержащий искомое значение и затем использует указатель для считывания строки.Если же используется нестрогое равенство (например LIKE, BETWEEN и т.д.) то вначале находится первая строка удовлетворяющая условию а затем считываются строки по порядку и переход между листьями осуществляется напрямую, без нового обхода по дереву.

    Указатель на строку – это rowid. Rowid — это псевдостолбец закрытого формата, который имеет каждая строка в каждой таблице. Внутри значения зашифрован указатель на физический адрес строки. Так как rowid не является частью стандарта SQL то он не видим при написании обычных запросов. Но вы можете выбирать эти значения и использовать их при необходимости. Это отображено на рисунке 7-3.

    79

    Rowid для каждой строки полностью уникальный. Каждая строка во всей БД имеет свой уникальный rowid. Расшифровав rowid получаем физический адрес строки, и Oracle может рассчитать в каком файле и где внутри файла находится искомая строка.

    B* Tree индексы очень эффективны для вычитки строк число которых невелико относительно всех строк таблицы и таблица достаточно большая. Рассмотрим запрос

     

    select count(*) from employees where last_name between ‘A%’ and ‘Z%’;

     

    При использовании такого условия в WHERE запрос вернёт все строки таблицы. Использование индекса при таком запросе будет значительно медленее чем чтение всей таблицы. И вообще – вся таблица это то что нужно в этом запросе. Другим примером будет настолько маленькая таблица где одна операция чтения считывает её полностью; тогда нет смысла считывать вначале индекс. Обычно говорят что запросы, результат которых предполагает вычитку более чем 2-4% данных в таблице обычно работают быстрее используя полное чтение таблицы. Особым случаем является значение NULL в столбце указанном в секции WHERE. Значение NULL не хранится в B* Tree индексах и запросы типа

    select * from employees where last_name is null;

    всегд будут использовать полное чтение. Немного смысла создавать B* Tree индекс для столбцов содержащих несколько уникальных значений, так как он не будет в достаточной степени селективным: количество строк для каждого уникального значения будет слишком высоко относительно количества строк всей таблицы. В общем, B* Tree индексы полезно использовать если

    Мощность (кратность – количество уникальных значений) столбца велика и

    Количество строк в таблице большое и

    Столбец используется в директивах WHERE и операциях объединения

    Bitmap индексы

    Во многих приложения природа данных и запросы таковы что использование B* Tree индексов не сильно помогает. Расммотрим пример. Есть таблица продаж, в которой набор данных о продажах в супермаркетах за год, которые нужно проанализировать в нескольких измерениях. На рисунке 7-4 показана простая диаграмма сущность-связь для четырёх измерений.

    80

    Мощность каждого измерения очень низкая. Преположим

    81

    Всего два измерения (DATE и PRODUCT) предполагают селективность лучше чем упомянутые 2-4%, т.е. делают использование индексов оправданным. Но если запросы используют предикаты группы (к примеру месяц в году, или группа товаров в которую входит десять товаров) то и эти измерения не подходят к требованиям. Отсюда следует простой факт: B* Tree индексы часто бесполезны в хранилищах данных. Типичным запросов может быть сравнение продаж между двумя магазинами приходящим покупателям определённой группы товаров за месяц. Можно создать B* Tree индесы для этих столбцов но Oracle проигнорирует их так как они недостаточно селективны. Для таких ситуация созданы bitmap индексы. Bitmap индексы хранят все rowid строк как битовую маску для каждого уникального значения ключа. Битовые маски индекса для измерения CHANNEL может быть к примеру

    82

    Это значит что первые две строки были приходящими покупателями, затем покупка с доставкой и т.д

    Битовые маски индекса столбца SHOP могут быть

    83

    Это значит что первые две продажи были в Лондоне, затем одна в Оксфорде, затем четвертая в Рединге и так далее.

    Теперь если приходит запрос

    select count(*) from sqles where channel=’WALK-IN’ and shop=’OXFORD’

    Oracle может выбрать две битовые маски и объединить их с помощью операции И

    84

    Результат логического И показывает что только седьмая и шестнадцатая строки удовлетворяют запросу. Операции над битовыми масками очень быстрые и могут использоваться для сложных булевых операций надо многими столбцами со многими сочетаниями И, ИЛИ или НЕ. Также достоинством bitmap индексов является то, что они хранят значения NULL. С точки зрения битовой маски – NULL просто ещё одно уникальное значение со своей битовой маской.

    В общем, bitmap индексы полезны когда

    Мощность столбца низкая и

    Количество строк в таблице большое и

    Столбец используется в операциях булевой алгебры

     

    TIP

    If you knew in advance what the queries would be, then you could build B*Tree indexes that would work, such as a composite index on SHOP and CHANNEL. But usually you don’t know, which is where the dynamic merging of bitmaps gives great flexibility.

    Свойства индексов

    Всего доступно шесть свойств которые можно применить при создании индекса

    • Уникальность / Unique или nonunique
    • Реверсивность / Reverse key
    • Сжатие / Compessed
    • Составной или нет /Composite
    • Основанный на функции или нет / Function based
    • Сортировка по возрастанию или убыванию / Ascending или descending

    Все шесть свойств можно применить к B* Tree индексам и только три последних можно использовать для bitmap индексов.

    Уникальный индекс не позволит дублировать значение. По умолчанию значение nonunique. Свойство уникальности индекса не связано с ограниченями уникальности или первичного ключа: если существует уникальный индекс  то вствка дубликатов невозможно даже при отстуствии ограничения уникальности.

    Реверсивный индекс строится на значениях ключа в которых байты строятся в обратном порядке: вместо индексирования значения к примеру ‘John’ будет использоваться значение ‘nhoJ’. Когда выполнится команда SELECT, Oracle автоматически преобразует строку поиска. Это используется для распределения строк по индексу в мультипользовательских системах. Например если много пользователей добавляют много строк в таблицу с первичным ключом как последовательно-увеличивающийся номер – все строки будут стремиться к концу индекса. Путем реверса ключа строки распределяются по всему индексу. При использовании индекса с реверсированным ключом базы данных не сохраняет ключи индекса друг за другом в лексикографическом порядке. Таким образом, когда в запросе присутствует предикат неравенства, ответ получается медленнее, поскольку база данных вынуждена выполнять полное сканирование таблицы. При индексе с реверсированным ключом база данных не может запустить запрос по диапазону ключа индекса.

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

    Составной индекс – это индекс который строится для нескольких столбцов. Нет ограничений на использование столбцов разных типов данных. Если условие WHERE не использует все столбцы, то индекс всё ещё может быть использован, но если не используется самый левый столбец, то Oracle использует skip-scanning метод который гораздо менее эффективный чем если бы левый столбец был включен.

    Основанный на функции индекс строится для результата выполнения функции к одному или нескольким столбцам, к примеру upper(last_name или to_char(startdate,’ccyy-mm-dd’). Запросы должны использовать ту же функцию для поиска или Oracle не сможет использовать индекс.

    По умолчанию индексы отсортированы по возрастанию (ascending), т.е. значения ключа хранятся от меньшего к большему. Режим по убыванию (descending) меняет это на противоположное. Фактически эта разница не очень важна: записи в индексе хранятся как двойной связный список т.е. можно переходить вверх или вниз с одинаковой скоростью, однако это повлияет на порядок строк в результате.

    Создание и использование индексов

    Индексы создаются неявно при создании ограничений первичного ключа или уникальности если индексы на соответствующих столбцах ещё не существуют. Синтаксис для явного создания индекса

     

    CREATE [UNIQUE | BITMAP] INDEX [ schema.]indexname

    ON [schema.]tablename (column [, column…] ) ;

     

    По умолчанию индекс не уникальный, без сжатия, не-реверсивный типа B* Tree. Невозможно создать уникальный битмап индекс (и не стоит этого поделать если вы подумаете об этом с точки зрения свойства селективности). Индексы это объекты схемы и возможно создать индекс в одной схеме и таблицу в другой, но большинство людей найдут такой способ странным. Составной индекс – это индекс для нескольких столбцов. Составные индексы могут быть созданы для столбцов разных типов и столбцы не обязательно следовать друг за другом.

     

    TIP

    Many database administrators do not consider it good practice to rely on implicit index creation. If the indexes are created explicitly, the creator has full control over the characteristics of the index, which can make it easier for theDBA to manage subsequently.

     

    Рассмотрим пример создания таблиц, индексов и затем определение ограничений

     

    create table dept(deptno number,dname varchar2(10));

    create table emp(empno number, surname varchar2(10),

    forename varchar2(10), dob date, deptno number);

    create unique index dept_i1 on dept(deptno);

    create unique index emp_i1 on emp(empno);

    create index emp_i2 on emp(surname,forename);

    create bitmap index emp_i3 on emp(deptno);

    alter table dept add constraint dept_pk primary key (deptno);

    alter table emp add constraint emp_pk primary key (empno);

    alter table emp add constraint emp_fk

    foreign key (deptno) references dept(deptno);

    Первые два индекса помечены как UNIQUE, что значит нельзя добавить дубликат. Это не определяет ограничение, но на самом деле это не что иное. Третий индекс не UNIQUE и позволяет хранить дубликаты и это составной индекс для двух столбцов. Четвертый индекс – это bitmap индекс, так как ожидается что мощность столбца будет низкой.

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

    После создания индексы работают абсолютно невидимо и автоматически. Перед выполнением SQL запроса, сервер Oracle оценит возможные пути выполнения. Некоторые способы будут использовать индексы, некоторые нет. Далее Oracle использует информацию которую он собирает автоматически о таблица и окружении для принятия решения какой способ предпочтителен.

     

    TIP

    The Oracle server should make the best decision about index use, but if it is getting it wrong, it is possible for a programmer to embed instructions, known as optimizer hints, in code that will force the use (or not) of certain indexes

    Изменение и удаление индексов

    Команда ALTER INDEX не может менять свойства индексов интересных с точки зрения программиста: тип, столбцы и всё иное. ALTER INDEX создана для администратора БД и обычно будет использоваться для управления физическими свойствами индекса. Если необходимо изменить логические свойства – то единственным способом будет удаление старого индекса и создание нового. К примеру чтобы изменить индекс EMP_I2 можно выполнить следующие команды

     

    drop index emp_i2;

    create index emp_i2 on emp(surname,forename,dob);

     

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

  • Создание и использование временных таблиц

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

     

    CREATE GLOBAL TEMPORARY TABLE temp_tab_name

    (column datatype [,column datatype] )

    [ON COMMIT {DELETE | PRESERVE} ROWS] ;

     

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

    Временные таблицы во многом схожи с обычными. Вы можете выполнять DML или SELECT команды используя их. Можно создать индексы, ограничеиня и триггеры для этих таблицы. Для них можно создать представления, синонимы и все SQL команды к временным таблицам будут выполняться быстрее чем к обычным таблицам.

    Первой причиной такого быстродействия является то что временные таблицы не являются сегментами в табличном пространстве. В идеале они существуют только в PGA сессии которая их использует, т.е. не используется жесткий диск и даже буфер БД. Если PGA не доступно памяти для хранения временной таблицы (к примеру если миллионы строк вставляются в таблицы – такое часто бывает при создании отчётов) то таблица записывается во временное табличное пространтсво. Операции чтения/записи во временном табличном пространстве гораздо быстрее так как не используется буфер БД: все операции производятся соотствующим серверным процессом сразу на диск.

    Вторая причина быстродействия – DML команды дял временных таблиц не создают вектором изменений. Так как данные хранятся только во время жизни сессии (или транзакции) нет смысла создавать данные для отмены изменений. Это даёт двойной выигрыш – быстрое выполнение DML операция и снижение нагрузки на систему генерации отмены изменений, что могло бы плохо влиять на производительность в нагруженной системе с большим числом пользователей.

    На рисунке 7-2 показано создание временной таблицы используя SQL *Plus. Также можно создать временную таблицу используя Database Control Table Creation Wizrad.

    77

  • Создание простой таблицы

    Таблицы могут храниться в базе данных несколькими способами. Простейший из них это heap таблица. Таблица может хранить строки различной длины в случайном порядке. Может быть какая-либо корреляция между порядком записи и порядком в котором данные хранятся но на это нельзя рассчитывать. Более сложные структуры таблицы, такие как представленные ниже, могут влиять на порядок и группировку строк или использовать случайное распределение

    74

    Использование сложных структур таблицы не вляет на SQL. Любой SQL запрос выполненные к таблицам созданным с этими опциями вернёт абсолютно одинаковый результат такой же как будто таблица являются обычными heap таблицами. Но если программист понимает как они работаеют то можно существенно увеличить производительность.

    Создание таблицы с указанием столбцов

    Для создания таблицы используется следующий синтаксис

     

    CREATE TABLE [schema.]tablename [ORGANIZATION HEAP]

    (columnname datatype [DEFAULT expression]

    [,columnname datatype [DEFAULT expression]);

     

    Как минимум необходимо указать имя таблицы (по умолчанию таблицы создаются в схеме текущего аккаунта, если вы не указываете другую) и один столбец с типом данных. Практически никто не указывает явно ORGANIZATION HEAP, так как это значение по умолчанию и стандарт SQL. Ключевое слово DEFAULT в определении столбца позволяет указать выражение которое будет использоваться при выполнении команды INSERT если не указано значение для этого столбца.

    Рассмотрим пример

     

    CREATE TABLE SCOTT.EMP

    (EMPNO NUMBER(4),

    ENAME VARCHAR2(10),

    HIREDATE DATE DEFAULT TRUNC(SYSDATE),

    SAL NUMBER(7,2),

    COMM NUMBER(7,2) DEFAULT 0.03);

     

    Создаем таблицу EMP в схеме SCOTT. Этот запрос выполнится если подключен аккаунт SCOTT (и тогда указание схемы необязательно) или другой пользователь у которого есть доступ на создание таблиц в схеме SCOTT. Столбцы

    EMPNO число длиной 4 цифры без дробной части. Если будет указана дробная часть при выполнении команды INSERT значение будет округлено к ближайшему целому.

    ENAME строка с максимальной длиной в 10 символов

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

    SAL предназначен для хранения зарплаты, принимает число длиной 7 цифр. Цифры после семи будут обрезаны а значение округлено

    COMM имеет значение по умолчанию 0.03, которое будет использоваться если явно не указанть значение при выполнении команды INSERT

    После создания таблицы можно записывать данные и выполнять команду SELECT

    75

    Обратите внимание что столбцы не указанные при команде INSERT использовали значения указанные в DEFAULT. Если бы не были указаны значения по умолчанию в секции DEFAULT использовалось бы значение NULL. Также обратите внимание что значение SAL было округлено.

     

    TIP

    The DEFAULT clause can be useful, but it is of limited functionality. You cannot use a subquery to generate the default value: you can only specify literal values or functions.

    Создание таблиц с использованием подзапросов

    Вместо создания таблиц с нуля и затем вставки строк (как мы только что рассмотрели) таблицы можно создавать используя другие таблицы – с помозью подзапросов. Этот метод позволяет определить таблицу и записать данные используя один запрос. Любой запрос может быть использован как источник для описания столбцов и как набор данных (строк). Синтаксис команды

     

    CREATE TABLE [schema.]tablename AS subquery;

     

    Любой запрос возвращает двумерный набор строк; результат хранится как новая таблица. Просто пример создания таблицы используя подзапрос

     

    create table employees_copy as select * from employees;

     

    Данный запрос создаст таблицу EMPLOYEES_COPY, которая будет полной копией таблицы EMPLOYEES, идентичной как по структуре столбцов и так и хранящихся строк. Все проверки на обязательность значения и значения по умолчанию также применяются к новой таблице, но первичные ключи, уникальные или внешние ключи не создаются (ограничения constraints обсудим далее), потому что эти три типа ограничений требуют создания индексов которые могут быть не нужными пользователю.

    Рассмотрим более сложный пример

     

    create table emp_dept as select

    last_name ename,department_name dname,round(sysdate — hire_date) service

    from employees natural join departments order by dname,ename;

     

    Строки в новой таблице это результат объекдинения двух таблиц-источников, и два столбца изменяют название. Новый столбец SERVICE будет заполнен результатом арифметической операции и получено количество дней с момента приёма на работу. Строки буду вставлены в определённом порядке. Этот порядок не будет управляться последующими DML командами, но предполагая что в схеме HR данные по умолчанию новая таблица будет выглядеть следующим образом

    76

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

     

    create table no_emps as select * from employees where 1=2;

    Изменение таблицы после создания

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

     

    • Добавление столбца
      alter table emp add (job_id number);

     

    • Изменение столбца
      alter table emp modify (commission_pct number(4,2) default 0.05);

     

    • Удаление столбца
      alter table emp drop column commission_pct;

     

    • Маркировка столбца как неиспользуемого
      alter table emp set unused column job_id;

     

    • Переименование столбца
      alter table emp rename column hire_date to recruited;

     

    • Включение режима только-чтения для таблицы
      alter table emp read only;

    Все изменения этих DDL команд содержат встроенный COMMIT. Эти изменения нельзя отменить и они не смогут отработать если имеется активная транзакция к таблице. Также эти операции происходят практически мгновенно за исключением удаления столбца. Удаление столбца может затребовать длительное время так как поскольку удаляется столбец, каждая строка должна быть изменена. Команда SET UNUSED делает столбец недоступным для SQL команд, и часто является более лучшей альтернативой в сочетании с командой

    ALTER TABLE tablename DROP UNUSED COLUMNS;

    которая удалит все неиспользуемы столбцы за один проход таблицы.

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

    Удаление и сокращение таблицы (drop и truncate)

    Команда TRUNCATE TABLE удаляет вссе строки из таблицы и оставляет определение таблицы. Команда DROP TABLE удаляет также и определение таблицы (саму таблицу вместе с данными). Синтаксис команды

     

    DROP TABLE [schema.]tablename ;

     

    Если схема не указана удалится таблица с этим именем в схеме текущего аккаунта.

    SQL не выдает предупреждений перед выполнением команды DROP и как и любая команда DDL содержит встроенный COMMIT. То есть удаление таблицы нельзя отменить. При выполнении определенных условий, удаление может быть отменено используя определенные методики.

    У команды DROP есть несколько ограничений: команда не выполнится если существует транзакция к этой таблице, или если данные этой таблицы используются как внешний ключ другой таблица, тогда для удаления исходной таблица необходимо вначале удалить ссылающуюся таблица или внешний ключ.

  • Объекты БД

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

    Типы объектов

    Запрос ниже отображает тип объекта и количество объектов этого типа в БД

    select object_type,count(object_type) from dba_objects group by object_type order by object_type;

    64

    Этот запрос использует представление DBA_OBJECTS, в котором хранится строка для каждого объекта БД. БД промышленных систем могут хрнаить сотни тысяч объектов. У вас может не быть доступна к DBA_OBJECTS в зависимости от прав и альтернативой является представление USER_OBJECTS, которое отобразит все принадлежащие вам объекты и представление ALL_OBJECTS, в котором доступны все объекты к которым у вас есть доступ. У всех пользователей есть доступ к этим представлениям.

    Для SQL программиста наиболее интересны объекты которые содержат, или можно сказать дают доступ к данным. Это таблицы, представления, синонимы, индексы и сиквенсы.

    Таблицы обычно хранят данных как строки состоящие из столбцов. Представления хранят результат команды SELECT который может использоваться как будет это таблица. Это обычный запрос но вместо того чтобы выполнять запрос и потом обрабатывать его, пользователь использует команду  SELECT к представлению. Т.е. пользователь выполняет запрос к результату другого запроса. Синоним это псевдоним к таблице (или представлению). Пользователь может выполнять запрос используя синоним а БД преобразует запрос к объекту на который указывает синоним. Назначение индексов — это увеличение скорости доступа к строкам в таблице. Если запросу необходима всего одна строка, то вместо просмотра всей таблицы для поиска нужной строки, индекс может предоставить указатель на расположение именно необходимой строки. Конечно, необходимо простматривать индекс для поиска нужной строки, но это обычно гораздо быстрее чем просмотр таблицы. Сиквенс используется для генерации уникальных чисел. Существует много сценариев когда необходимы уникальные числа. Сиквенсы генерируют числа по порядку и по запросу: т.е. абсолютно невозможна генерация дубликата. Остальные типы объектов используются гораздо реже. Их используют в основном PL/SQL разработчики и администраторы.

    Именование объектов схемы

    Объекты в схеме принадлежат пользователю владельцу схемы и должны удовлетворять следующим требованиям

    Длина от 1 до 30 символов (за исключением названий ссылок БД, длина которых ограничена 128 символами)

    Нельзя использовать зарезервированные слова (такие как SELECT)

    Имя должно начинаться с буквы

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

    Буквы автоматически конвертируются в прописные

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

    65

    EXAM TIP

    Object names must be no more than 30 characters. The characters can be letters, digits, underscore, dollar, or hash.

    Также такие инструменты как SQL *PLUS и SQL Developer автоматически конвертируют буквы к верхнему регистру если имя не указано в двойных кавычках. Надо помнить что имена регистрочувствительны. В следующем примере две таблицы абсолютно разные

    66

    Пространства имен

    Обычно говорят что уникальным идентификатором объекта является имя объекта с именем схемы. Это в принципе так, но для полного понимания именования необходимо ввести понятие пространство имен. Пространство имём определяет группу типов объектов, внутри которой все именя должны определяться уникальным идентификатором – схемой и именем. Объекты в разных пространствах имен могут иметь одинаковые имена.

    Эти типы объектов используют общее пространство имен

    67

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

    Эти объекты каждый хранится в своём пространтсве имен

    68

    То есть возможно (но это явно не стоит делать) создать индекс с таким же именем как имя таблицы даже в той же схеме.

    EXAM TIP

    Within a schema, tables, views, and synonyms cannot have the same names.

    Типы данных

    Во время создания таблицы каждому столбцу необходимо назначить тип данных, который определяет природу данных доступных для хранения в этом столбце. Эти типы данных также использутся для определения аргументов PL/SQL процедур и функций. Когда вы выбираете тип данных для столбца, вы должны обдумать какие данные вы будете хранить и какие операции вам необходимо осуществлять. Размер тоже имеет значение: некоторые типы данных имеют фиксированную длину, занимая одно и то же количество байт вне зависимости от того какие данные фактически записаны; другие имеют различную длину. Если столбец не заполнен, Oracle не будет выделять пространство совсем. Если вы затем обновите строку заполнив этот столбец, размер строки станет больше вне зависимости от типа данных (фиксированный размер или изменяемый).

    Следующие типы данных использутся для алфавитно-цифровых данных

    69

    TIP

    For ISO/ANSI compliance, you can specify a VARCHAR data type, but any columns of this type will be automatically converted to VARCHAR2.

    Следующие типы данных используются для хранения численных данных, все изменяемой длины

    70

    Далее типы данных для даты и время, все фикисированного размера

    71

    Типы данных для хранения больших объектов

    72

    Типы данных RAW и ROWID

    73

    Тип VARCHAR2 должен иметь фиксированное число определяющее максимальную длину столбца. Если значение записанное в этот столбец меньше чем это значение, не проблема: значение займёт столько места сколько необходимо. Если значение имеет большее длину чем максимальная допустимая длина столбца – запрос вернёт ошибку. Если значение обновляется на более длинное или короткое – размер столбца (и соответственно строки) также будет изменяться. Если значение не определено или обновляется в значение NULL то столбец не занимает место совсем.

    У типа данных NUMBER может быть указана точность (precision)и шкала (scale).Точность обозначает максимальное количество цифр в числе, а шкала определяет сколько цифр число хранит после запятой. Если шкала имеет отрицательное значение – то все цифры порядков меньше либо равных этому значению заменяются на ноль. Если количество цифр превышает точность – то возникнет ошибка. Если количество цифр не превышает точность, но количество цифр после запятой больше чем шкала – значение будет округлено к ближайшему значению удовлетворяющему шкале.

    Тип данных DATE всегда включает в сеюя век, год, месяц, день, час, минуту и секунду – даже если они не указываются в момент вставки. Год, месяц и день – должны быть указаны; если час, минута и секунда не указаны явно использутся значение по умолчанию – полночь.