министерство образования и науки российской - Северо...

142
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ «СЕВЕРО-КАВКАЗСКИЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ» ɆȿɌɈȾɂɑȿɋɄɂȿ ɍɄȺɁȺɇɂə ɤ ɜɵɩɨɥɧɟɧɢɸ ɥɚɛɨɪɚɬɨɪɧɵɯ ɪɚɛɨɬ ɩɨ ɞɢɫɰɢɩɥɢɧɟ ©ɌȿɏɇɈɅɈȽɂɂ ɉɊɈȽɊȺɆɆɂɊɈȼȺɇɂə ȻȺɁ ȾȺɇɇɕɏª ɞɥɹ ɫɬɭɞɟɧɬɨɜ ɫɩɟɰɢɚɥɶɧɨɫɬɢ 10.05.03 Ʉɨɦɩɥɟɤɫɧɨɟ ɨɛɟɫɩɟɱɟɧɢɟ ɢɧɮɨɪɦɚɰɢɨɧɧɨɣ ɛɟɡɨɩɚɫɧɨɫɬɢ ɚɜɬɨɦɚɬɢɡɢɪɨɜɚɧɧɵɯ ɫɢɫɬɟɦ ɋɬɚɜɪɨɩɨɥɶ

Transcript of министерство образования и науки российской - Северо...

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ

ФЕДЕРАЦИИ ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ

ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ

ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ

«СЕВЕРО-КАВКАЗСКИЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ»

МЕТОДИЧЕСКИЕ УКАЗАНИЯ

к выполнению лабораторных работ по дисциплине

«ТЕХНОЛОГИИ ПРОГРАММИРОВАНИЯ БАЗ ДАННЫХ»

для студентов специальности

10.05.03 Комплексное обеспечение информационной

безопасности автоматизированных систем

Ставрополь, 2013

Методические указания составлены с учетом требований ГОС ВПО по

специальности 10.05.03 Информационная безопасность автоматизированных

систем и в полном соответствии с программой дисциплины и рекомендуются

студентам специальности 10.05.03 Информационная безопасность

автоматизированных систем при изучении дисциплины «Технологии

программирования баз данных».

Составитель доц. Лапина М.А.

Рецензент к. т. н., доцент Чипига А. Ф.

Содержание

ЛАБОРАТОРНАЯ РАБОТА № 1. ОПЕРАТОР CREATE ................................... 4

ЛАБОРАТОРНАЯ РАБОТА № 2. ОПЕРАТОР SELECT .................................. 17

ЛАБОРАТОРНАЯ РАБОТА № 3. ОПЕРАТОРЫ РЕДАКТИРОВАНИЯ

ДАННЫХ ............................................................................................................... 35

ЛАБОРАТОРНАЯ РАБОТА № 4. ОПЕРАТОРЫ РЕДАКТИРОВАНИЯ

ДАННЫХ ............................................................................................................... 44

ЛАБОРАТОРНАЯ РАБОТА № 4. ОПЕРАТОРЫ РЕДАКТИРОВАНИЯ

ДАННЫХ ............................................................................................................... 52

ЛАБОРАТОРНАЯ РАБОТА 5. СОЗДАНИЕ БАЗЫ ДАННЫХ ....................... 60

ЛАБОРАТОРНАЯ РАБОТА 6. СВЯЗИ И ИНДЕКСЫ ..................................... 78

ЛАБОРАТОРНАЯ РАБОТА 7. КЛАССИФИКАТОРЫ .................................. 118

ЛАБОРАТОРНАЯ РАБОТА 8. СОЗДАНИЕ СЛОЖНОЙ БАЗЫ ДАННЫХ 126

ЛАБОРАТОРНАЯ РАБОТА 9. ПРОГРАММИРОВАНИЕ КУРСОРОВ ...... 133

ЛАБОРАТОРНАЯ РАБОТА 10. ДОПОЛНИТЕЛЬНЫЕ ВОЗМОЖНОСТИ ПО

ОБРАБОТКЕ ДАННЫХ ..................................................................................... 137

СПИСОК РЕКОМЕНДУЕМОЙ ЛИТЕРАТУРЫ ............................................. 142

ЛАБОРАТОРНАЯ РАБОТА № 1. ОПЕРАТОР CREATE

Краткая справка: Создание объектов базы данных осуществляется с

помощью операторов языка определения данных DDL.

Таблицы базы данных создаются с помощью команды CREATE

TАBLE. Эта команда создает пустую таблицу, то есть таблицу, не имеющую

строк. Значения в эту таблицу вводятся с помощью команды INSERT.

Команда CREATE TАBLE определяет имя таблицы и множество

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

быть определен тип и размер. Каждая создаваемая таблица должна иметь, по

крайней мере, один столбец. Синтаксис команды CREATE TАBLE имееи

следующий вид:

CREATE TАBLE <имя таблицы>

(<имя столбца><тип данных>[(<размер>]);

Используемые в SQL типы данных как минимум поддерживают

стандарты ANSI:

CHAR (CHARACTER),

INT (INTEGER),

SMALLINT,

DEC (DECIMAL),

NUMERIC,

FLOAT…

Тип данных, для которого обязательно должен быть указан

размер, - это CHAR. Реальное количество символов, которое может

находиться в поле, изменяется от нуля (если в поле содержится NULL –

значение) до заданного в CREATE TАBLE максимального значения.

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

создать таблицу STUDENT.

CREATE TАBLE STUDENT 1

(STUDENT_ID INTEGER,

SURNAME VARCHAR (60),

NAME VARCHAR (60),

STIPEND DOUBLE,

KURS INTEGER,

CITY VARCHAR (60),

BIRTHDAY DATE,

UNIV_ID INTEGER);

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

Операции поиска-выборки (SELECT) данных из таблиц по

значениям их полей могут быть существенно ускорены путем использования

индексации данных. Индекс содержит упорядоченный (в алфавитном или

числовом порядке) список содержимого столбцов или группы столбцов в

индексируемой таблице с идентификаторами этих строк (ROWID). Для

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

представляют собой способ логического упорядочения значений

индексированных столбцов, позволяющего, в отличие от последовательного

перебора строк, существенно повысить скорость доступа к конкретным

строкам таблицы при выборках, использующих значения этих столбцов.

Индексация позволяет находить содержащий индексированную строку блок

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

При использовании индексации следует, иметь в виду, что

управление индексом существенно замедляет время выполнения операций,

связанных с обновлением данных (таких, как INSERT, DELETE), так как эти

операции требуют перестройки индексов.

Индексы можно создавать как по одному, так и по множеству

полей. Если указано более одного поля для создания единственного индекса,

данные упорядочиваются по значениям первого поля, по которому

осуществляется индексирование. Внутри получившейся группы

осуществляется упорядочение по значениям второго поля, для получившихся

в результате групп осуществляется упорядочение по значениям третьего поля

и т.д.

Синтаксис команды создания индекса имеет следующий вид:

CREATE INDEX <имя индекса> ON <имя таблицы> (<имя столбца>

[,<имя столбца>]);

При этом таблица должна быть уже создана и содержать

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

определенное в команде, должно быть уникальным в базе данных. Будучи

однажды созданным, индекс является невидимым для пользователя, все

операции с ним осуществляет СУБД.

ПРИМЕР

Если таблица EXAM_MARKS часто используется для поиска

конкретного студента по значению поля STUDENT_ID, то следует создать

индекс по этому полю.

CREATE INDEX STUDENT_ID_1 ON EXAM_MARKS

(STUDENT_ID);

Для удаления индекса (при этом обязательно требуется знать его имя)

используется команда DROP INDEX, имеющая следующий синтаксис:

DROP INDEX <имя индекса>;

Удаление индекса не изменяет содержимого поля или полей, индекс

которых удаляется.

Изменение существующей таблицы.

Для модификации структуры и параметров существующей

таблицы используется команда ALTER TABLE. Синтаксис команды ALTER

TABLE для добавления столбцов в таблицу имеет вид

ALTER TABLE <имя таблицы> ADD (<имя столбца> <тип

данных><размер>);

По этой команде для существующих в таблице строк добавляется

новый столбец, в который заносится NULL – значение. Этот столбец

становится последним в таблице. Можно добавлять несколько столбцов, в

этом случае их определения в команде ALTER TABLE разделяются запятой.

Возможно изменение описания столбцов. Часто это связано с

изменением размеров столбцов, добавлением или удалением ограничений,

накладываемых на их значения. Синтаксис команды в этом случае имеет вид

ALTER TABLE <имя таблицы> MODIFY <имя столбца> <тип

данных><размер/точность>;

Следует иметь в виду, что модификация характеристик столбца

может осуществляться не в любом случае, а с учетос следующих

ограничений:

Изменение типа данных возможно только в том случае, если

столбец пуст;

Для незаполненного столбца можно изменять размер/точность.

Для заполненного столбца размер/точность можно увеличить, но нельзя

понизить;

Ограничение NOT NULL может быть установлено, если ни одно

значение в столбце не содержит NULL. Опцию NOT NULL всегда можно

отменить;

Разрешается изменять значения, установленные по умолчанию.

Удаление таблицы.

Чтобы удалить существующую таблицу, необходимо

предварительно удалить все данные из этой таблицы, то есть сделать ее

пустой. Таблица, имеющая строки, не может быть удалена. Синтаксис

команды, осуществляющей удаление пустой таблицы, имеет следующий вид:

DROP TABLE <имя таблицы>;

ЗАДАНИЕ № 1

1. Напишите команду CREATE TABLE для создания таблицы

LECTURER.

2. Напишите команду CREATE TABLE для создания таблицы

SUBJECT.

3. Напишите команду CREATE TABLE для создания таблицы

UNIVERSITY.

4. Напишите команду CREATE TABLE для создания таблицы

EXAM_MARKS.

5. Напишите команду CREATE TABLE для создания таблицы

SUBJ_LECT.

6. Напишите команду, которая позволит быстро выбрать данные о

студентах по курсам, на которых они учатся.

7. Создайте индекс, который позволит для каждого студента быстро

осуществить поиск оценок, сгруппированных по датам.

Ограничения на множество допустимых значений данных.

До сих пор рассматривалась только следующее ограничение –

значения, вводимые в таблицу, должны иметь типы данных и размеры,

совместимые с типами/размером данных столбцов, в которые эти значения

вводятся (как определено в команде CREATE TABLE или ALTER TABLE).

Описание таблицы может быть дополнено более сложными ограничениями,

накладываемыми на значения, которые могут быть вставлены в столбец или

группу столбцов. Ограничения (CONSTRAINTS) являются частью

определения таблицы.

При создании (изменении) таблицы могут быть определены

ограничения на вводимые значения. В этом случае SQL будет отвергать

любое из них при несоответствии заданным критериям. Ограничения могут

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

вставляемых в столбец (CHECK, NOT NULL). Они могут иметь связь со

всеми значениями столбца, ограничивая новые строки значениями, которые

не содержатся в столбцах или их наборах (уникальные значения, первичные

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

находящимися в другой таблице, допуская, например, вставку в столбец

только тех значений, которые в данный момент содержатся также в другом

столбце другой или этой же таблицы (внешний ключ). Эти ограничения

носят динамический характер.

Существует два основных типа ограничений – ограничения на

столбцы и ограничения на таблицу. Ограничения на столбцы (COLUMN

CONSTRAINTS) применимы только к отдельным столбцам, а ограничения на

таблицу (TABLE CONSTRAINTS) применимы к группам, состоящим из

одного или более столбцов. Ограничения на столбец добавляются в конце

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

описания столбца (запятой). Ограничения на таблицу размещаются в конце

определения таблицы, после определения последнего столбца. Команда

CREATE ТABLE имеет следующий синтаксис, расширенный включением

ограничений:

CREATE ТABLE <имя таблицы>

(<имя столбца> <тип данных> <ограничения на столбец>,

<имя столбца> <тип данных> <ограничения на столбец>,

<ограничения на таблицу> (<имя столбца>[,<имя столбца>]));

Поля, заданные в круглых скобках после описания ограничений таблицы, -

это поля, на которые эти ограничения распространяются. Ограничения на

столбцы применяются к тем столбцам, в которых они описаны.

Ограничение NOT NULL.

Чтобы запретить возможность использования в поле NULL-

значений, можно при создании таблицы командой CREATE ТABLE указать

для соответствующего столбца ключевое слово NOT NULL. Это ограничение

применимо только к столбцам таблицы. Как уже отмечалось, NULL – это

специальный маркер, обозначающий факт, что поле пусто. Но он полезен не

всегда.

Первичные ключи, например, в принципе не должны содержать

NULL – значений (быть пустыми), поскольку это нарушило бы требование

уникальности первичного ключа (более строго – функциональную

зависимость атрибутов таблицы от первичного ключа). Во многих других

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

определенные значения. Если ключевое слово NOT NULL размещается

непосредственно после типа данных (включая размер) столбца, то любые

попытки оставить значение поля пустым (ввести в поле NULL – значение)

будут отвергнуты системой.

Например, для того, чтобы в определении таблицы STUDENT

запретить использование NULL – значений для столбцов STUDENT_ID,

SURNAME, NAME, можно записать следующее:

CREATE TАBLE STUDENT

(STUDENT_ID INTEGER NOT NULL,

SURNAME CHAR (25) NOT NULL,

NAME CHAR (10) NOT NULL,

STIPEND INTEGER,

KURS INTEGER,

CITY CHAR (15),

BIRTHDAY DATE,

UNIV_ID INTEGER);

Важно помнить: если для столбца указано NOT NULL, то при

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

значение, вводимое в это поле. При отсутствии ограничения NOT NULL в

столбце значение может отсутствовать, если только не указано значение

столбца по умолчанию (DEFAULT). Если при создании таблицы ограничение

NOT NULL не было указано, то его можно указать позже, используя команду

ALTER TABLE. Однако для того, чтобы для вновь вводимого с помощью

команды ALTER TABLE столбца можно было задать ограничение NOT

NULL, таблица, в которую добавляется столбец, должна быть пустой.

Уникальность как ограничение на столбец.

Иногда требуется, чтобы все значения, введенные в столбец,

отличались друг от друга. Например, этого требуют первичные ключи.

Если при создании таблицы для столбца указывается ограничение

UNIQUE, то база данных отвергает любую попытку ввести в это поле какой-

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

ограничение применимо только к тем полям, которые были объявлены NOT

NULL.

Можно предложить следйющее определение таблицы STUDENТ,

использующее ограничение UNIQUE:

CREATE TАBLE STUDENT

(STUDENT_ID INTEGER NOT NULL UNIQUE,

SURNAME CHAR (25) NOT NULL,

NAME CHAR (10) NOT NULL,

STIPEND INTEGER,

KURS INTEGER,

CITY CHAR (15),

BIRTHDAY DATE,

UNIV_ID INTEGER);

Объявляя поле STUDENT_ID уникальным, можно быть

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

одинаковыми идентификаторами.

Столбцы, отличные от первичного ключа, для которых требуется

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

уникальными ключами (CANDIDATE KEYS, UNIQUE KEYS).

Уникальность как ограничение таблицы.

Можно сделать уникальными группу полей, указав UNIQUE в

качестве ограничений таблицы. При объединении полей в группу важен

порядок, в котором они указываются.

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

студент сдавал в один день более одного экзамена, то можно в таблице

объявить уникальной комбинацию значений полей STUDENT_ID и

EXAM_DATE. Для этого следует создать таблицу EXAM_MARKS

следующим способом:

CREATE TАBLE EXAM_MARKS

(EXAM__ID INTEGER NOT NULL,

STUDENT_ID INTEGER NOT NULL,

SUBJ_ID INTEGER NOT NULL,

MARK CHAR (1),

EXAM_DATE DATE NOT NULL,

UNIQUE (STUDENT_ID, EXAM_DATE));

Обратите внимание, что оба поля в ограничении таблицы UNIQUE все

еще используют ограничение столбца - NOT NULL. Если бы использовалось

ограничение столбца UNIQUE для поля STUDENT_ID, то такое ограничение

таблицы было бы необязательным.

Если значение поля STUDENT_ID должно быть различным для

каждой строки в таблице EXAM_MARKS, это можно сделать, объявив

UNIQUE как ограничение самого поля STUDENT_ID. В этом случае не будет

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

EXAM_DATE. Следовательно, указание UNIQUE как ограничение таблицы

наиболее полезно использовать в случаях, когда не требуется уникальность

индивидуальных полей, как это имеет место на самом деле в

рассматриваемом примере.

Присвоение имен ограничениям.

Ограничениям таблиц можно присваивать уникальные имена.

Преимущество явного задания имени ограничения состоит в том, что в этом

случае при выдаче системой сообщения о нарушении установленного

ограничения будет указано его имя, что упрощает обнаружение ошибок.

Для присвоения имени ограничению используется несколько

измененный синтаксис команд CREATE TABLE, ALTER TABLE.

Приведенный выше пример запроса изменяется следующим

образом:

CREATE TАBLE EXAM_MARKS

(EXAM__ID INTEGER NOT NULL,

STUDENT_ID INTEGER NOT NULL,

SUBJ_ID INTEGER NOT NULL,

MARK CHAR (1),

EXAM_DATE DATE NOT NULL,

CONSTRAINT STUD_SUBJ_CONSTR

UNIQUE (STUDENT_ID, EXAM_DATE));

В этом запросе STUD_SUBJ_CONSTR – это имя, присвоенное

указанному ограничению таблицы.

Ограничение первичных ключей.

Первичные ключи таблицы – это специальные случаи

комбинирования ограничений UNIQUE и NOT NULL. Первичные ключи

имеют следующие особенности:

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

Внешние ключи по умолчанию ссылаются на первичный ключ

таблицы;

Первичный ключ является идентификатором строк таблицы

(строки, однако, могут идентифицироваться и другими способами).

Улучшенный вариант создания таблицы STUDENT 1 с объявленным

первичным ключом имеет следующий вид:

CREATE TАBLE STUDENT

(STUDENT_ID INTEGER PRIMARY KEY,

SURNAME CHAR (25) NOT NULL,

NAME CHAR (10) NOT NULL,

STIPEND INTEGER,

KURS INTEGER,

CITY CHAR (15),

BIRTHDAY DATE,

UNIV_ID INTEGER);

Составные первичные ключи.

Ограничение PRIMARY KEY может также быть применено для

нескольких полей, составляющих уникальную комбинацию значений –

составной первичный ключ.

Можно применить ограничение таблицы PRIMARY KEY,

объявив пару EXAM__ID и STUDENT_ID первичным ключом таблицы.

CREATE TАBLE NEW_EXAM_MARKS

(STUDENT_ID INTEGER NOT NULL,

SUBJ_ID INTEGER NOT NULL,

MARK INTEGER,

DATE DATE,

CONSTRAINT EX_PR_KEY PRIMARY KEY (EXAM_ID,

STUDENT_ID));

Проверка значений полей.

Ограничение CHECK позволяет определять условие, которому должно

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

принято. Любая попытка обновить или заменить значение поля такими, для

которых предикат, задаваемый ограничением CHECK, имеет значение ложь,

будет отвергаться.

Рассмотрим таблицу STUDENT. Значение столбца STIPEND в

этой таблице выражается десятичным числом. Наложим на значение этого

столбца ограничение – величина размера стипендии должна быть меньше

200.

Соответствующий запрос имеет следующий вид:

CREATE TАBLE STUDENT

(STUDENT_ID INTEGER PRIMARY KEY,

SURNAME CHAR (25) NOT NULL,

NAME CHAR (10) NOT NULL,

STIPEND INTEGER CHECK (STIPEND <200),

KURS INTEGER,

CITY CHAR (15),

BIRTHDAY DATE,

UNIV_ID INTEGER);

Проверка ограничивающих условий с использованием составных

полей.

Ограничение CHECK можно использовать в качестве табличного

ограничения, то есть при необходимости включить более одного поля в

ограничивающее условие.

Предположим, что ограничение на размер стипендии (меньше

200) должно распространяться только на студентов, живущих в Воронеже.

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

CHECK:

CREATE TАBLE STUDENT

(STUDENT_ID INTEGER PRIMARY KEY,

SURNAME CHAR (25) NOT NULL,

NAME CHAR (10) NOT NULL,

STIPEND INTEGER,

KURS INTEGER,

CITY CHAR (15),

BIRTHDAY DATE,

UNIV_ID INTEGER UNIQUE,

CHECK (STIPEND < 200 AND CITY = ‘ Воронеж ‘));

или в несколько другой записи:

CREATE TАBLE STUDENT

(STUDENT_ID INTEGER PRIMARY KEY,

SURNAME CHAR (25) NOT NULL,

NAME CHAR (10) NOT NULL,

STIPEND INTEGER,

KURS INTEGER,

CITY CHAR (15),

BIRTHDAY DATE,

UNIV_ID INTEGER UNIQUE,

CHECK (STIPEND < 200 AND CITY = ‘ Воронеж ‘));

Установка значений по умолчанию.

Значение поля по умолчанию указывается в команде CREATE

TАBLE тем же способом, что и ограничение столбца, с помощью ключевого

слова DEFAULT<значение по умолчанию>.

Опция DEFAULT не имеет ограничительного свойства, так как

она не ограничивает значения, вводимые в поле, а просто конкретизирует

значение поля в случае, если оно не было задано.

Предположим, что основная масса студентов, информация о

которых находится в таблице STUDENT, проживает в Воронеже. Чтобы при

задании атрибутов не вводить для большинства студентов название города

«Воронеж», можно установить его как значение поля CITY по умолчанию,

например:

CREATE TАBLE STUDENT

(STUDENT_ID INTEGER PRIMARY KEY,

SURNAME CHAR (25) NOT NULL,

NAME CHAR (10) NOT NULL,

STIPEND INTEGER CHECK (STIPEND < 200),

KURS INTEGER,

CITY CHAR (15) DEFAULT ‘ Воронеж ‘,

BIRTHDAY DATE,

UNIV_ID INTEGER);

ЗАДАНИЕ № 2.

1. Создайте таблицу EXAM_MARKS так, чтобы не допускался ввод

в таблицу двух записей об оценках одного студента по конкретному экзамену

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

любым предметам в один день.

2. Создайте таблицу предметов обучения SUBJECT так, чтобы

количество отводимых на предмет часов по умолчанию было равно 36, не

допускались записи с отсутствующим количеством часов, поле SUBJ_ID

являлось первичным ключом таблицы и значения семестров (поле

SEMESTER) лежали в диапазоне от 1 до 12.

3. Создайте таблицу EXAM_MARKS таким образом, чтобы

значения поля EXAM__ID были больше значений поля SUBJ_ID, а значение

поля SUBJ_ID были больше значений поля STUDENT_ID; пусть также будут

запрещены значения NULL в любом из этих трех полей.

ЛАБОРАТОРНАЯ РАБОТА № 2. ОПЕРАТОР SELECT

Выборка данных. Простейшие select-запросы

Оператор SELECT (выбрать) языка SQL является самым важным и

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

информации из таблиц базы данных. Упрощенный синтаксис оператора

SELECT выглядит следующим образом. SELECT [DISTINСТ] <список

атрибутов>

FROM <список таблиц >

[WHERE <условие выборки>]

[ORDER BY <список атрибутов >]

[GROUP BY <список атрибутов >]

[HAVING <условие>]

[UNION <выражение с оператором SELECT >] ;

В квадратных скобках указаны элементы, которые могут отсутствовать в

запросе.

Ключевое слово SELECT сообщает базе данных, что данное предложение

является запросом на извлечение информации. После слова SELECT через

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

которых запрашивается.

Обязательным ключевым словом в предложении-запросе является слово

FROM (из). За ключевым словом FROM указывается список разделенных

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

Например,

SELECT NAME, SURNAME

FROM STUDENT;

Любой SQL - запрос должен заканчиваться символом “;” (точка с

запятой).

Приведенный запрос осуществляет выборку всех значений полей NAME

и SURNAME из таблицы STUDENT.

Его результатом является таблица следующего вида:

NAME SURNAME

E

Иван

Иванов

Петр

Петров

Вадим

Сидоров

Борис

Кузнецов

Ольга

Зайцева

Андрей

Павлов

… Порядок следования столбцов в этой таблице соответствует порядку

полей NAME, SURNAME, указанному в запросе, а не их порядок во входной

таблице STUDENT.

Если необходимо вывести значения всех столбцов таблицы, то можно

вместо перечисления их имен использовать символ “*” (звездочка).

SELECT*

FROM STUDENT;

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

STUDENT.

Еще раз обратите внимание на то, что получаемые в результате SQL –

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

отношения. В частности, в них могут оказаться кортежи (строки) с

одинаковыми значениями атрибутов.

Например, запрос “Получить список названий городов, где проживают

студенты, сведения о которых находятся в таблице STUDENT, можно

записать в следующем виде.

SELECT CITY FROM STUDENT;

Для исключения из результата SELECT – запроса повторяющихся

записей используется ключевое слово DISTINCT (отличный). Если запрос

SELECT извлекает множество полей, то DISTINCT исключает дубликаты

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

Предыдущий запрос можно записать в следующем виде.

SELECT DISTINCT CITY

FROM STUDENT;

Ключевое слово ALL (все), в отличие от DISTINCT, оказывает

противоположное действие, то есть при его использовании

повторяющиеся строки включаются в состав выходных данных. Режим,

задаваемый ключевым словом ALL, действует по умолчанию, поэтому в

реальных запросах для этих целей оно практически не используется.

Использование в операторе SELECT предложения, определяемого

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

(предикат), принимающее значение истина или ложь для значений полей

строк таблиц, к которым обращается оператор SELECT. Предложение

WHERE определяет, какие строки указанных таблиц должны быть

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

те строки, для которых условие (предикат), указанное в предложении

WHERE, принимает значение истина.

Пример

Написать запрос, выполняющий выборку имен (NАМЕ) всех студентов с

фамилией (SURNAME) Петров, сведения о которых находятся в таблице

STUDENT.

SELECT SURNAME, NАМЕ

FROM STUDENT

WHERE SURNAME = ‘ПЕТРОВ ‘;

В ЗАДАВАЕМЫХ В ПРЕДЛОЖЕНИИ WHERE условиях могут

использоваться операции сравнения, определяемые операторами = (равно),

< (меньше), > (больше), а также логические операторы AND, OR, NOT.

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

использованы операторы in, between, like, IS null.

Операторы in (равен любому из списка) и not in (не равен ни одному из

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

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

оператора IN.

Построенный с использованием in предикат (условие) считается

истинным, если значение поля, имя которого указано слева от IN,

совпадает (подразумевается точное совпадение) с одним из значений,

перечисленных в списке, указанном в скобках справа от IN.

Предикат, построенный с использованием NOT IN, считается

истинным, если значение поля, имя которого указано слева от NOT IN, не

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

скобках справа от NOT IN.

Примеры

Получить из таблицы exam_marks сведения о студентах, имеющих

экзаменационные оценки только 4 и 5.

SELECT *

FROM EXAM_MARKS

WHERE MARK IN (4, 5 );

Получить сведения о студентах, не имеющих ни одной

экзаменационной оценки, равной 4 и 5.

SELECT *

FROM EXAM_MARKS

WHERE MARK NOT IN (4, 5 );

Оператор between используется для проверки условия вхождения

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

атрибута этот оператор задает границы его изменения.

Например, запрос на вывод записей о предметах, на изучение которых

отводится количество часов, находящееся в пределах между 30 и 40, имеет

вид:

select *

from subject

where hour between 30 and 40;

Граничные значения, в данном случае значения 30 и 40, входят во

множество значений, с которыми производится сравнение. Оператор

between может использоваться как для числовых, так и для символьных

типов полей.

Оператор LIKE применим только к символьным полям типа char или

VARCHAR.

Этот оператор просматривает строковые значения полей с целью

определения, входит ли заданная в операторе like подстрока (образец

поиска) в символьную строку-значение проверяемого поля.

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

можно применять шаблон искомого образца строки, использующий

следующие символы:

• символ подчеркивания «__», указанный в шаблоне, определяет

возможность наличия в указанном месте одного любого символа;

• символ «%» допускает присутствие в указанном месте проверяемой

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

Пример

Написать запрос, выбирающий из таблицы student сведения о

студентах, фамилии которых начинаются на букву «Р».

SELECT *

FROM STUDENT

WHERE SURNAME LIKE 'P%';

В случае необходимости включения в образец самих символов «_» и

«%» применяют так называемые escape-символы. Если escape-символ

предшествует знаку «__» и «%», то эти знаки будут восприниматься

буквально. Например, можно задать образец поиска с помощью

следующего выражения:

LIKE '_/_P' ESCAPE ‘\’.

В этом выражении символ «\» с помощью ключевого слова ESCAPE

объявляется escape-символом. Первый символ «_» в заданном шаблоне

поиска '_\_P'_ будет соответствовать, как и ранее, любому символу в

проверяемой строке. Однако второй символ «_», следующий после

символа «\», объявленного escape-символом, уже будет

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

'Р' в заданном шаблоне.

Обращаем внимание на то, что рассмотренные выше операторы

сравнения «=, <, >, <=, >=, <>» и операторы IN, between и like ни в коем

случае нельзя использовать для проверки содержимого поля на наличие в

нем пустого значения NULL. Для этих целей предназначены специальные

операторы is null (является пустым) и IS not null (является не пустым).

В SQL реализованы операторы преобразования данных и встроенные

функции, предназначенные для работы со значениями столбцов и/или

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

запросах везде, где допустимы выражения.

Числовые, символьные и строковые константы

Несмотря на то, что SQL работает с данными в понятиях игрок и

столбцов таблиц, имеется возможность применения течений выражений,

построенных с использованием встроенных функций, констант, имен

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

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

Если в запросе вместо спецификации столбца SQL обнаруживает

число, то оно интерпретируется как числовая константа.

Символьные константы должны указываться в одинарных кавычках.

Если одинарная кавычка должна выводиться как часть строковой

константы, то ее нужно предварить другой одинарной кавычкой.

Например, результатом выполнения запроса

SELECT 'Фамилия', SURNAME, 'Имя', NAME, 100

FROM STUDENT;

является таблица следующего вида:

SURNAME NAME

Фамилия Иванов Им

я

Иван 100

Фамилия Петров Им

я

Петр 100

Фамилия Сидоров Им

я

Вадим 100

Фамилия Кузнецов Им

я

Борис 100

Фамилия Зайцева Им

я

Ольга 100

Фамилия Павлов Им

я

Андрей 100

Фамилия Котов Им

я

Павел 100

Фамилия Лукин Им

я

Артем 100

Фамилия Петров Им

я

Антон 100

Фамилия Белкин Им

я

Вадим 100

Арифметические операции для преобразования числовых данных

• Унарный (одиночный) оператор «-» (знак минус) изменяет знак

числового значения, перед которым он указан, на противоположный.

• Бинарные операторы «+», «-», «*» и «/» предоставляют возможность

выполнения арифметических операций сложения, вычитания, умножения и

деления.

Например, результат запроса

SELECT SURNAME, NAME, STIPEND, -(STIPEND*KURS)/2 FROM

STUDENT

WHERE KURS = 4 AND STIPEND > 0;

Выглядит следующим образом:

SURNAM

E

NAME STIPEND KURS

Сидоров Вадим 150 4 -300

Петров Антон 200 4 -400 Операция конкатенации строк

Операция конкатенации «||» позволяет соединять («склеивать»)

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

констант в одну строку.

Эта операция имеет синтаксис

<значимое символьное выражение > {||} <значимое символьное

выражение

Например:

SELECT SURNAME || '_' || NAME, STIPEND

FROM STUDENT

WHERE KURS = 4 AND STIPEND > 0;

Результат запроса будет выглядеть следующим образом:

STIPEND

Сидоров_Вадимм 150

Петров Антон 200

Функции преобразования символов в строке

• lower перевод в строчные символы (нижний регистр)

lower (<строка>)

• upper перевод в прописные символы (верхний регистр)

upper (<строка>)

• INITCAP перевод первой буквы каждого слова строки в прописную

(заглавную)

initcap (<строка>)

Например:

SELECT LOWER (SURNAME), UPPER (NAME)

FROM STUDENT

WHERE KURS = 4 AND STIPEND > 0;

Результат запроса будет выглядеть следующим образом:

SURNAM

EMME

NAME

сидоров ВАДИ

М петров АНТО

Н

Строковые функции

• lpad дополнение строки слева

lpad (<строка>,<длина>[,<подстрока>])

• <строка> дополняется слева заданной в <подстроке>

последовательностью символов до указанной <длины> (возможно, с

повторением последовательности);

• если <подстрока> не указана, то по умолчанию <строка>дополняется

пробелами;

• если <длина> меньше длины <строки>, то исходная<строка> усекается

слева до заданной <длины>.

• rpad - дополнение строки справа

rpad (<строка>,<длина>[,<подстрока>])

• <строка> дополняется справа заданной в <подстроке>

последовательностью символов до указанной <длины> (возможно, с

повторением последовательности);

• если <подстрока> не указана, то по умолчанию <строка>дополняется

пробелами;

• если <длина> меньше длины <строки>, то исходная <строка>усекается

справа до заданной <длины >.

• ltrim удаление левых граничных символов

ltrim (<строка>[,<подстрока>])

• из <строки> удаляются слева символы, указанные в <подстроке>;

• если <подстрока> не указана, по умолчанию удаляются пробелы;

• в <строку> справа добавляется столько пробелов, сколько символов

слева было удалено, то есть длина <строки> остается неизменной.

• RTRIM удаление правых граничных символов rtrim

(<строка>[,<подстрока>])

• из <строки> удаляются справа символы, указанные в <подстроке>;

• если <подстрока> не указана, по умолчанию удаляются пробелы;

• в <строку> слева добавляется столько пробелов, сколько символов

справа было удалено, то есть длина <строки> остается неизменной.

Функции ltrim и rtrim рекомендуется использовать при написании

условных выражений, в которых сравниваются текстовые строки. Дело в

том, что наличие начальных или конечных пробелов в сравниваемых

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

Например, константы ' ААА' и 'ААА ' не равны друг другу.

• SUBSTR выделение подстроки

SUBSTR (<строка>,<начало>[,<количество>])

• из <строки> выбирается заданное <количество> символов, начиная с

указанной параметром <начало> позиции в строке;

• если <количество> не задано, символы выбираются с <начала> и до

конца <строки>;

• возвращается подстрока, содержащая число символов, заданное

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

параметром <начало> до конца строки;

• если указанное <начало> превосходит длину <строки>, то возвращается

строка, состоящая из пробелов. Длина этой строки будет равна заданному

Количеству > или исходно длине <строки> (при не заданном

<количестве>).

• INSTR - ПОИСК ПОДСТРОКИ

instr (<строка>,<подстрока>[,<начало поиска> [,<номер вхождения>]])

• <начало поиска> задает начальную позицию в строке для поиска

<подстроки>. Если не задано, то по умолчанию принимается значение 1;

• <номер вхождения> задает порядковый номер искомой подстроки. Если

не задан, то по умолчанию принимается значение 1;

• значимые выражения в <начале поиска> или в <номере вхождения >

должны иметь беззнаковый целый тип или приводиться к этому типу;

• тип возвращаемого значения int;

функция возвращает позицию найденной подстроки.

• length - определение длины строки

LENGTH(<строкa>)

• длина <строки>, тип возвращаемого значения int;

• функция возвращает NULL, если <строка> имеет NULL-значение.

Примеры запросов, использующих строковые функции

Результат запроса

SELECT LPAD (SURNAME, 10, '<@>'), RPAD (NAME, 10,'$')

FROM STUDENT

WHERE KURS = 3 AND STIPEND > 0;

будет выглядеть следующим образом:

@@@@Петров Петр$$$$$$

@@@@Павлов Андрей$$$$

@@@@@Лукин Артем$$$$$

А запрос

SELECT SUBSTR(NAME, 1, 1) || ‘.’|| SURNAME, CITY,

LENGTH (CITY)

FROM STUDENT

WHERE KURS IN(2, 3, 4) AND STIPEND > 0;

выдаст результат:

CITY

П. Петров Курск 5

С.

Сидоров

Москва 6

О.

Зайцева

Липецк 6

А. Лукин Вороне

ж

7

А. Петров NULL NU

LL

Функции работы с числами

* ABS абсолютное значение

ABS (<значимое числовое выражение>)

* FLOOR урезает значение числа с плавающей точкой до

наибольшего целого, не превосходящего заданное число

FLOOR (<значимое числовое выражение>)

CEIL самое малое целое, равное или большее заданного числа

CEIL (<значимое числовое выражение>)

• Функция округления round

round (<значимое числовое выражение>,<точность>)

аргумент <точность> задает точность округления (см. пример ниже)

• Функция усечения trunc

trunc (значимое числовое выражение>,<точность>)

• Тригонометрические функции cos, sin, tan

cos (<значимое числовое выражение>)

SIN (<значимое числовое выражение>)

tan (<значимое числовое выражение>)

• Гиперболические функции cosh, sinh, tanh

cosh (<значимое числовое выражение>)

sinh (<значимое числовое выражение>) tanh (<значимое числовое

выражение>)

• Экспоненциальная функция ехр

ЕХР (<значимое числовое выражение>)

• Логарифмические функции ln, LOG

ln (<значимое числовое выражение>)LOG (<значимое числовое

выражение>)

• Функция возведения в степень - power

power (<значимое числовое выражение>,<экспонента>)

• Определение знака числа sign

SIGN (<значимое числовое выражение>)

• Вычисление квадратного корня sqrt

sqrt (<значимое числовое выражение>)

Пример

Запрос

SELECT UNIV_NAME, RATING, ROUND (RATING, -1), ТRUNC

(RATING, -1)

FROM UNIVERSITY;

вернет результат:

UNIV_NAME RATIN

G

МГУ 606 6

10

60

0 ВГУ 296 3

00

29

0 НГУ 345 3

50

34

0 РГУ 416 4

20

41

0 БГУ 326 3

30

32

0 ТГУ 368 3

70

36

0 ВГМА 327 3

30

32

0

Функции преобразования значений

• Преобразование в символьную строку TO_CHAR

to_char (<значимое выражение>[,<символьный формат>])

• <значимое выражение > числовое значение или значение типа дата-

время;

• для числовых значений <символьный формат> должен иметь синтаксис

[S]9[9][.9[9]], где S представление знака числа (при отсутствии

предполагается без отображения знака), 9 представление цифр-знаков

числового значения (для каждого знакоместа). Символьный формат

определяет вид отображения чисел. По умолчанию для числовых значений

используется формат '999999.99';

• для значений типа дата-время <символьный формат>имеет вид (то есть

вид отображения значений даты и времени):

- в части даты

'DD-Mon-YY'

'DD-Mon-YYYY'

'MM/DD/YY'

'MM/DD/YYYY'

'DD.MM.YY'

'DD.MM.YYYYf

- в части времени

'НН24'

‘НН24:МI'

‘HH24:MI:SS'

'HH24:MI:SS.FF’

где: НН24 часы в диапазоне от 0 до 24

MI - минуты

SS - секунды

FF - тики (сотые доли секунды)

При выводе времени в качестве разделителя по умолчанию

используется двоеточие (:), но при желании можно использовать любой

другой символ.

Возвращаемое значение символьное представление Значимого

выражения> в соответствии с заданным <символьным форматом >

преобразования.

• Преобразование из символьного значения в числовое- _ to_number

to_number (<значимое символьное выражение>)

При этом <значимое символьное выражение> должно задавать

символьное значение числового типа.

• Преобразование символьной строки в дату to date

то_date (<значимое символьное выражение>[,<символьный формат>])

• <значимое символьное выражение> должно задавать символьное

значение типа дата-время;

• <символьный формат> должен описывать представление значения типа

дата-время в <значимом символьном выражении. Допустимые форматы (в

том числе и формат по умолчанию) приведены выше.

Возвращаемое значение <значимое символьное выражение> во

внутреннем представлении. Тип возвращаемого значения date.

Над значениями типа date разрешены следующие операции

• бинарная операция сложения;

• бинарная операция вычитания.

В бинарных операциях один из операндов должен иметь значение

отдельного элемента даты: только год, или только месяц, или только день.

Например:

• при добавлении к дате '22.05.1998' пяти лет получится дата '22.05.2003';

• при добавлении к этой же дате девяти месяцев получится дата

'22.02.1998';

• при добавлении 10 дней получим '01.06.1998'.

При сложении двух полных дат, например, '22.05.1998' и '01.12.2000',

результат непредсказуем.

Пример

Запрос

SELECT SURNAME, NAME, BIRTHDAY,

TO_CHAR (BIRTHDAY, 'DD-MON-YYYY'),

TO_CHAR (BIRTHDAY, 'DD.MM.YY')

FROM STUDENT;

вернет результат:

SURNAME NAME BIRTHDAY

Иванов Иван 3/12/1982 З-дек-1982 3.12.82

Петров Петр 1/12/1980 1-дек-1980 1.12.80

Сидоров Вадим 7/06/1979 7-июн-1979 7.06.79

Кузнецов Борис 8/12/1981 8-дек-1981 8.12.81

Зайцева Ольга 1/05/1981 1 -май- 1981 1.05.81

Павлов Андрей 5/11/1979 5-ноя-1979 5.11.79

Котов Павел NULL NULL NULL

Лукин Артем 1/12/1981 1-дек-1981 1.12.81

Петров Антон 5/08/1981 5-авг-1981 5.08.81

Белкин Вадим 7/01/1980 7-янв-1980 7.01.80

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

одного типа в другой. Синтаксис этой команды имеет вид

САSТ<значимое выражение> as <тип данных>

• <значимое выражение> должно иметь числовой или символьный тип

языка SQL (возможно, с указанием длины, точности и масштаба) или быть

NULL-значениeм;

• любое числовое выражение может быть явно преобразовано в любой

другой числовой тип;

• символьное выражение может быть преобразовано в любой числовой

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

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

значение по правилам языка SQL;

• если явно заданная длина символьного типа недостаточна и

преобразованное значение не размещается в нем, то результативное

значение усекается справа;

• возможно явное преобразование символьного типа в символьный с

другой длиной. Если длина результата больше длины аргумента, то

значение дополняется пробелами; если меньше, то усекается;

• NULL-значение преобразуется в NULL-значение соответствующего

типа;

• числовое выражение может быть преобразовано в символьный тип.

Пример

SELECT CAST STUDENT_ID AS CHAR(10)

FROM STUDENT;

ЗАДАНИЕ №1.

В предложенной учебной базе данных, состоящей из ряда таблиц,

выполните следующие задания.

1. Напишите запрос для вывода идентификатора (номера) предмет

обучения, его наименования, семестра, в котором он читается и

количества отводимых на этот, предмет часов для всех строк таблицы

SUBJECT .

2.Напишите запрос, позволяющий вывести все строки таблицы

ЕХАМ_МАRКS, в которых предмет обучения имеет номер (SUBJ ID);

равный 12.

3.Напишите запрос, выбирающий все данные из таблицы STUDENT,

расположив столбцы таблицы в следующем порядке: KURS, SURNAME,

NAME, STIPEND.

4. Напишите запрос SELECT, который выводит наименование пред

мета обучения (SUBJ NAME) и количество часов (HOUR) для каждого

предмета (SUBJECT) в 4-м семестре (SEMESTR).

5. Напишите запрос, позволяющий получить из таблицы ЕХАМ_МАRКS

значения столбца МАRK (экзаменационная оценка) для всех студен

тов, исключив из списка повторение одинаковых строк.

6. Напишите запрос, который выводит список фамилий студентов,

обучающихся на третьем и последующих курсах.

7. Напишите запрос, выбирающий данные о фамилии, имени и но

мере курса для студентов, получающих стипендию больше 140.

8. Напишите запрос, выполняющий выборку из таблицы SUBJECT

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

часов.

9. Напишите запрос, который выполняет вывод списка университе

тов, рейтинг которых превышает 300 баллов.

10. Напишите запрос к таблице STUDENT для вывода списка фамилий

(SURNAME), имен (NAME) и номера курса (KURS) всех студентов со

стипендией, большей или равной 100, и живущих в Москве.

11. Какие данные будут получены в результате выполнения запроса?

SELECT *

FROM STUDENT

WHERE (STIPEND < 100 OR

NОТ (В1RТНDАУ >= '10/03/1980'

AND STUDENT ID > 1003”;

12. Какие данные будут получены в результате выполнения запроса?

SELECT * FROM STUDENT

WHERE NОТ ((В1RТНDАУ = '10/03/1980' OR STIPEND > 100)

AND STUDENT ID >= 1003);

STUDENT (Студент)

STUDET SURNAME NAME STIPEND KURS CITY BIRTHDAY UNIV

_ ID _ID

1 Иванов Иван 150 1 Орел 3/12/1982 10

3 Петров Петр 200 3 Курск 1/12/1980 10

6 Сидоров Вадим 150 4 Москва 7/06/1979 22

10 Кузнецов Борис 0 2 Киев 8/12/1981 10

12 Зайцева Ольга 250 2 Москва 5/11/1979 10

265 Павлов Андрей 0 3 Ростов 1/11/1981 10

32 Котов Павел 150 5 Москва NULL 14

654 Лукин Артем 200 3 Ростов 1/12/1981 10

276 Петров Антон 200 4 NULL 5/08/1981 10

SUBJECT (Предмет обучения)

STUDET

_ ID

SUBJ_NAME HOUR SEMESTER

10 Информатика 56 1

22 Физика 34 1

43 Математика 56 2

56 История 34 4

94 Английский 56 3

73 Физкультура 34 5

EXAM_MARKS (Экзаменационные оценки)

EXAM_

ID

STUDET_

ID

SUBJ-

ID

MARK EXAM_DATE

145 12 10 5 12/01/2000

34 32 10 4 23/01/2000

75 55 10 5 05/01/2000

238 12 22 3 17/06/2000

639 55 22 NULL 22/06/20000

43 6 22 4 18/01/2000

Задание № 2.

1. Напишите запрос на вывод находящихся в таблице EXAM_MARKS

номеров предметов обучения, экзамены по которым сдавались между 10 и 20

января 1999 года.

2. Напишите запрос, выбирающий данные обо всех предметах

обучения, экзамены по которым сданы студентами, имеющими

идентификаторы 12 и 32.

3. Напишите запрос на вывод названий предметов обучения,

начинающихся на букву «И».

4. Напишите запрос, выбирающий сведения о студентах, у которых

имена начинаются на буквы «И» или «С».

5. Напишите запрос для выбора из таблицы EXAM_MARKS записей, в

которых отсутствуют значения оценок (поле MARK).

6. Напишите запрос на вывод из таблицы EXAM_MARKS записей,

имеющих в поле MARK значения оценок.

ЗАДАНИЕ №3.

1. Составьте запрос для таблицы student таким образом, чтобы

выходная таблица содержала один столбец, содержащий последовательность

разделенных символом «;» (точка с запятой) значений всех столбцов этой

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

символами (верхний регистр), то есть быть представленными в следующем

виде: 10;КУЗНЕЦОВ;БОРИС;0;БРЯНСК;8/12/1981; 10.

2. Составьте запрос для таблицы student таким образом, чтобы

выходная таблица содержала всего один столбец в следующем виде:

Б.КУЗНЕЦОВ; место жительства - БРЯНСК; родился - 8.12.81.

3. Составьте запрос для таблицы student таким образом, чтобы

выходная таблица содержала всего один столбец в следующем виде: б.

кузнецов; место жительства - брянск; родился:8-дек-1981.

4. Составьте запрос для таблицы student таким образом, чтобы

выходная таблица содержала всего один столбец в следующем виде: Борис

Кузнецов родился в 1981 году.

5. Вывести фамилии, имена студентов и величину получаемых ими

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

6. То же, что и в задаче 4, но только для студентов 1, 2 и 4-го курсов и

таким образом, чтобы фамилии и имена были выведены прописными

буквами.

7. Составьте запрос для таблицы university таким образом, чтобы

выходная таблица содержала всего один столбец в следующем виде: Код-

10;ВГУ-г. ВОРОНЕЖ; Рейтинг = 296.

8. То же, что и в задаче 7, но значения рейтинга требуется округлить

до первого знака (например, значение 382 округляется до 400).

ЛАБОРАТОРНАЯ РАБОТА № 3. ОПЕРАТОРЫ РЕДАКТИРОВАНИЯ

ДАННЫХ

Краткая справка. Агрегирующие функции позволяют получать из

таблицы сводную (агрегированную) информацию, выполняя операции над

группой строк таблицы. Для задания в SELECT-запросе агрегирующих

операций используются следующие ключевые слова:

• COUNT определяет количество строк или значений поля, выбранных

посредством запроса и не являющихся NULL-значениями;

• SUM вычисляет арифметическую сумму всех выбранных значений

данного поля;

• AVG вычисляет среднее значение для всех выбранных значений

данного поля;

• МАХ вычисляет наибольшее из всех выбранных значений данного

поля;

• MIN вычисляет наименьшее из всех выбранных значений данного

поля.

В SELECT-запросе агрегирующие функции используются аналогично

именам полей, при этом последние (имена полей) используются в качестве

аргументов этих функций.

Функция AVG предназначена для подсчета среднего значения поля на

множестве записей таблицы.

Например, для определения среднего значения поля MARK (оценки) по

всем записям таблицы EXAM_MARKS можно использовать запрос с функцией

AVG следующего вида:

SELECT AVERAGE (MARK)

FROM EXAM_MARKS;

Для подсчета общего количества строк в таблице следует использовать

функцию COUNT со звездочкой.

SELECT COUNT(*)

FROM EXAM_MARKS;

Аргументы DISTINCT и ALL позволяют, соответственно, исключать и

включать дубликаты обрабатываемых функцией COUNT значений, при этом

необходимо учитывать, что при использовании опции ALL значения NULL все

равно не войдут в число подсчитываемых значений.

SELECT COUNT (DISTINCT SUBJ_ID)

FROM SUBJECT;

Предложение GROUP BYGROUP BY (группировать по) позволяет

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

поля, и применять агрегирующие функции уже не ко всем записям таблицы,

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

Предположим, требуется найти максимальное значение оценки,

полученной каждым студентом.

Запрос будет выглядеть следующим образом:

SELECT STUDENT_ID, MAX (MARK)

FROM EXAM_MARKS

GROUP BY STUDENT_ID;

Выбираемые из таблицы EXAM_MARKS записи группируются по

значениям поля STUDENT_ID, указанного в предложении GROUP BY, и для

каждой группы находится максимальное значение поля MARK. Предложение

GROUP BY позволяет применять агрегирующие функции к каждой группе,

определяемой общим значением поля (или полей), указанных в этом

предложении. В приведенном запросе рассматриваются группы записей,

сгруппированные по идентификаторам студентов.

В конструкции GROUP BY для группирования может быть

использовано более одного столбца. Например:

SELECT STUDENT_ID, SUBJ_ID, MAX(MARK)

FROM EXAM_MARKS

GROUP BY STUDENT_ID, SUBJ_ID;

В этом случае строки вначале группируются по значениям первого

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

Таким образом, GROUP BY не только устанавливает столбцы, по которым

осуществляется группирование, но и указывает порядок разбиения столбцов

на группы.

Следует иметь в виду, что в предложении GROUP BY должны быть

указаны все выбираемые столбцы, приведенные после ключевого слова

SELECT, кроме столбцов, указанных в качестве аргумента в агрегирующей

функции.

При необходимости часть сформированных с помощью GROUP BY

групп может быть исключена с помощью предложения HAVING.

Предложение HAVING определяет критерий, по которому Группы

следует включать в выходные данные, по аналогии с предложением WHERE,

которое осуществляет это для отдельных строк.

SELECT SUBJ_NAME, MAX(HOUR)

FROM SUBJECT

GROUP BY SUBJ_NAME

HAVING MAX (HOUR) >= 72;

В условии, задаваемом предложением HAVING, указывают только

поля или выражения, которые на выходе имеют единственное значение для

каждой выводимой группы.

Задание №1.

1. Напишите запрос для подсчета количества студентов, сдававших

экзамен по предмету обучения с идентификатором, равным 20.

2. Напишите запрос, который позволяет подсчитать в таблице

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

3. Напишите запрос, который выполняет выборку для каждого

студента значения его идентификатора и минимальной из полученных им

оценок.

4. Напишите запрос, осуществляющий выборку для каждого студента

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

5. Напишите запрос, выполняющий вывод фамилии первого в

алфавитном порядке (по фамилии) студента, фамилия которого начинается

на букву «И».

6.Напишите запрос, который выполняет вывод (для каждого предмета

обучения) наименования предмета и максимального значения номера

семестра, в котором этот предмет преподается.

7. Напишите запрос, который выполняет вывод данных для каждого

конкретного дня сдачи экзамена о количестве студентов, сдававших экзамен

в этот день.

8. Напишите запрос для получения среднего балла для каждого курса

по каждому предмету.

9. Напишите запрос для получения среднего балла для каждого

студента.

10. Напишите запрос для получения среднего балла для каждого

экзамена.

11. Напишите запрос для определения количества студентов,

сдававших каждый экзамен.

12. Напишите запрос для определения количества изучаемых

предметов на каждом курсе.

3.2 Пустые значения (NULL) в агрегирующих функциях

Наличие пустых (NULL) значений в полях таблицы определяет

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

следует учитывать в запросах.

Влияние NULL-значений в функции COUNT

Если аргумент функции COUNT является константой или столбцом без

пустых значений, то функция возвращает количество строк, к которым

применимо определенное условие или группирование.

Если аргументом функции является столбец, содержащий пустое

значение, то COUNT вернет число строк, которые не содержат пустые

значения и к которым применимо определенное в COUNT условие или

группирование.

Если бы механизм NULL не был доступен, то неприменимые и

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

КОНСТРУКЦИИ WHERE.

Поведение функции COUNT(*) не зависит от пустых значений. Она

возвратит общее количество строк в таблице.

Влияние NULL-значений в функции AVG

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

число элементов множества. Однако если некоторые элементы пусты (то есть

их значения неизвестны или не существуют), деление на количество всех

элементов множества приведет к неправильному результату.

Функция AVG вычисляет среднее значение всех известных значений

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

значений и делит ее на количество этих значений, а не на общее количество

значений, среди которых могут быть NULL-значения. Если столбец состоит

только из пустых значений, то функция AVG также возвратит NULL.

Результат действия трехзначных условных операторов

Условные операторы при отсутствии пустых значений возвращают

либо TRUE (истина), либо FALSE (ложь).

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

возвращено и третье значение: UNKNOWN (неизвестно). В этой схеме,

например, условие WHERE А = 2, где А - имя столбца, значения которого

могут быть неизвестны, при А = 2 будет соответствовать TRUE, при А = 4 в

результате будет получено значение FALSE, а при отсутствующем значении

А (NULL-значение) результат будет UNKNOWN.

Пустые значения оказывают влияние на использование логических

операторов NOT, AND и OR.

Оператор NOT

Обычный унарный оператор NOT обращает оценку TRUE в FALSE и

наоборот. Однако NOT NULL по прежнему будет возвращать пустое значение

NULL. При этом следует отличать случай NOT NULL от условия is NOT

NULL, которое является противоположностью IS NULL, отделяя известные

значения от неизвестных.

Оператор AND

• Если результат двух условий, объединенных оператором AND,

известен, то применяются правила булевой логики, то есть при обоих

утверждениях TRUE составное утверждение также будет TRUE. Если же хотя

бы одно из двух утверждений будет FALSE, то составное утверждение будет

FALSE.

• Если результат одного из утверждений неизвестен, а другой

оценивается как TRUE, то состояние неизвестного утверждения является

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

• Если результат одного из утверждений неизвестен, а другой

оценивается как FALSE, итоговый результат будет FALSE.

• Если результат обоих утверждений неизвестен, то результат также

остается неизвестным.

Оператор OR

• Если результат двух условий, объединенных оператором OR,

известен, то применяются правила булевой логики, а именно: если хотя бы

одно из двух утверждений соответствует TRUE, то и составное

утверждение будет TRUE, если оба утверждения оцениваются как FALSE, то

составное утверждение будет FALSE.

• Если результат одного из утверждений неизвестен, а другой

оценивается как TRUE, итоговый результат будет TRUE.

• Если результат одного из утверждений неизвестен, а другой

оценивается как FALSE, то состояние неизвестного утверждения имеет

определяющее значение. Следовательно, итоговый результат также

неизвестен.

• Если результат обоих утверждений неизвестен, то результат также

остается неизвестным.

Примечание

Отсутствующие (NULL) значения целесообразно использовать в

столбцах, предназначенных для агрегирования, чтобы извлечь преимущества

из способа обработки пустых значений в функциях COUNT и AVERAGE.

Практически во всех остальных случаях пустых значений следует избегать,

так как при их наличии существенно усложняется корректное построение

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

Для индикации же отсутствующих, неприменимых или по какой-то причине

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

устанавливаемые заранее (например, с помощью команды CREATE TABLE).

Упорядочение выходных полей (ORDER BY)

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

упорядочены.

Однако данные, выводимые в результате выполнения запроса, могут

быть упорядочены. Для этого используется оператор ORDER BY, который

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

одного или нескольких выбранных столбцов. При этом можно задать

возрастающую (ASC) или убывающую (DESC) последовательность

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

последовательность сортировки.

Запрос, позволяющий выбрать все данные из таблицы предметов

обучения SUBJECT с упорядочением по наименованиям предметов, выглядит

следующим образом:

SELECT *

FROM SUBJECT

ORDER BY SUBJ_NAME;

Тот же список, но упорядоченный в обратном порядке, Можно

получить запросом:

SELECT *

FROM SUBJECT

ORDER BY SUBJ _NAME DESC;

Можно упорядочить выводимый список предметов обучения по

значениям семестров, а внутри семестров по наименованиям предметов.

SELECT *

FROM SUBJECT

ORDER BY SEMESTER, SUBJ_NAME;

Предложение ORDER BY может использоваться с GROUP BY для

упорядочения групп записей. При этом оператор ORDER BY в запросе всегда

должен быть последним.

SELECT SUBJ_NAME, SEMESTER, MAX (HOUR)

FROM SUBJECT

GROUP BY SEMESTER, SUBJ_NAME

ORDER BY SEMESTER;

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

номера, имея, однако, в виду, что в данном случае это номера столбцов,

указанные при определении выходных данных в запросе, а не номера

столбцов в таблице. Полем с номером 1 является первое поле, указанное в

предложении ORDER BY - независимо от его расположения в таблице.

SELECT SUBJ_ID, SEMESTER

FROM SUBJECT

ORDER BY 2 DESC;

В этом запросе выводимые записи будут упорядочены по полю

SEMESTR.

Если в поле, которое используется для упорядочения, существуют

NULL-значения, то все они размещаются в конце или предшествуют всем

остальным значениям этого поля.

Задание №2.

1. Предположим, что стипендия всем студентам увеличена на 20%.

Напишите запрос к таблице STUDENT, выполняющий вывод

номера студента, фамилию студента и величину увеличенной

стипендии.

Выходные данные упорядочить: а) по значению последнего

столбца (величине стипендии); б) в алфавитном порядке фамилий

студентов.

2. Напишите запрос, который по таблице EXAM_MARKS позволяет

найти: а) максимальные и б) минимальные оценки каждого студента и

который выводит их вместе с идентификатором студента.

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

в порядке: а) убывания семестров и б) возрастания отводимых на предмет

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

должны следовать имя предмета обучения и идентификатор предмета.

4. Напишите запрос, который выполняет вывод суммы баллов всех

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

порядке убывания этих сумм.

5. Напишите запрос, который выполняет вывод: а) среднего, б)

минимального, в) максимального баллов всех студентов для каждой даты

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

этих значений.

ЛАБОРАТОРНАЯ РАБОТА № 4. ОПЕРАТОРЫ РЕДАКТИРОВАНИЯ

ДАННЫХ

Краткая справка

MySQL позволяет использовать одни запросы внутри других запросов,

то есть вкладывать запросы друг в друга.

Предположим, известна фамилия студента («Петров»), но неизвестно

значение поля STUDENT_ID для него. Чтобы извлечь данные обо всех оценках

этого студента, можно записать следующий запрос:

SELECT *

FROM EXAM_MARKS

WHERE STUDENT_ID =

(SELECT STUDENT_ID

FROM STUDENT SURNAME = 'Петров');

Как работает запрос со связанным подзапросом?

• Выбирается строка из таблицы, имя которой указано во внешнем

запросе.

• Выполняется подзапрос и полученное значение применяется для

анализа этой строки в условии предложения WHERE внешнего запроса.

• По результату оценки этого условия принимается решение о

включении или не включении строки в состав выходных данных.

• Процедура повторяется для следующей строки таблицы

внешнего запроса.

Следует обратить внимание, что приведенный выше запрос

корректен только в том случае, если в результате выполнения указанного в

скобках подзапроса возвращается единственное значение. Если в результате

выполнения подзапроса будет возвращено несколько значений, то этот

подзапрос будет ошибочным. В данном примере это произойдет, если в

таблице STUDENT будет несколько записей со значениями поля SURNAME =

'Петров'.

В некоторых случаях для гарантии получения единственного значения

в результате выполнения подзапроса используется DISTINCT. Одним из видов

функций, которые автоматически всегда выдают в результате единственное

значение для любого количества строк, являются агрегирующие функции.

Оператор IN также широко применяется в подзапросах. Он задает

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

истинности задаваемого этим оператором предиката.

Данные обо всех оценках (таблица EXAM_MARKS) студентов из

Воронежа можно выбрать с помощью следующего запроса:

SELECT * FROM EXAM_MARKS

WHERE STUDENT_ID IN

(SELECT STUDENT_ID

FROM STUDENT

WHERE CITY = 'Воронеж');

Подзапросы можно применять внутри предложения HAVING. Пусть

требуется определить количество предметов обучения с оценкой,

превышающей среднее значение оценки студента с идентификатором 301:

SELECT COUNT (DISTINCT SUBJ_ID), MARK

FROM EXAM_MARKS

GROUP BY MARK

HAVING MARK >

(SELECT AVG(MARK)

FROM EXAM_MARKS

WHERE STUDENT_ID = 301);

Формирование связанных подзапросов

При использовании подзапросов во внутреннем запросе можно

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

запроса. В этом случае такой связанный подзапрос выполняется по одному

разу для каждой строки таблицы основного запроса.

Пример: выбрать сведения обо всех предметах обучения, по которым

проводился экзамен 20 января 1999 г.

SELECT * FROM SUBJECT SU

WHERE '20/01/1999' IN

(SELECT EXAM_DATE

FROM EXAM_MARKS EX

WHERE SU.SUBJ_ID = EX.SUBJ_ID);

B некоторых СУБД для выполнения этого запроса может

потребоваться преобразование значения даты в символьный тип. В

приведенном запросе SU и EX являются псевдонимами (алиасами), то есть

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

данном запросе вместо настоящих имен.

В приведенном примере они используются вместо имен таблиц

SUBJECT И EXAM_MARKS.

Эту же задачу можно решить с помощью операции соединения таблиц:

SELECT DISTINCT SU.SUBJ_ID, SUBJ_NAME, HOUR, SEMESTER

FROM SUBJECT FIRST, EXAM_MARKS SECOND WHERE

FIRST.SUBJ_ID = SECOND.SUBJ_ID

AND SECOND. EXAM_DATE = '20/01/1999';

В этом выражении алиасами таблиц являются имена FIRST И SECOND.

Можно использовать подзапросы, связывающие таблицу со своей

собственной копией.

Например, надо найти идентификаторы, фамилии и стипендии студентов,

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

SELECT DISTINCT STUDENT_ID, SURNAME, STIPEND FROM

STUDENT El

WHERE STIPEND > (SELECT AVG (STIPEND) FROM STUDENT E2

WHERE El.KURS = E2.KURS);

Тот же результат можно получить с помощью следующего запроса:

SELECT DISTINCT STUDENT_ID, SURNAME, STIPEND FROM

STUDENT El,

(SELECT KURS, AVG (STIPEND) AS AVG_STIPEND FROM

STUDENT E2

GROUP BY E2.KURS) E3

WHERE El.STIPEND > AVG_STIPEND AND El.KURS=E3.KURS;

Обратите внимание - второй запрос будет выполнен гораздо быстрее.

Дело в том, что в первом варианте запроса агрегирующая функция AVG

выполняется над таблицей, указанной в подзапросе, для каждой строки

внешнего запроса.

В другом варианте вторая таблица (алиас Е2) обрабатывается

агрегирующей функцией один раз, в результате чего формируется

вспомогательная таблица (в запросе она имеет алиас ЕЗ), со строками

которой затем соединяются строки первой таблицы (алиас Е1). Следует

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

зависит от оптимизатора запросов конкретной СУБД.

Связанные подзапросы в HAVING

Ранее указывалось, что предложение GROUP BY позволяет

группировать выводимые SELECT-запросом записи по значению некоторого

поля.

Использование предложения HAVING позволяет при выводе

осуществлять фильтрацию таких групп. Предикат предложения HAVING

оценивается не для каждой строки результата, а для каждой группы

выходных записей, сформированной предложением GROUP BY внешнего

запроса.

Пусть, например, необходимо по данным из таблицы ЕXAM_MARKS

определить сумму полученных студентами оценок (значений поля MARK),

сгруппировав значения оценок по датам экзаменов и исключив те дни, когда

число студентов, сдававших в течение дня экзамены, было меньше 10.

SELECT EXAM_DATE, SUM (MARK)

FROM EXAM_MARKS A

GROUP BY EXAM_DATE

HAVING 10 <

(SELECT COUNT(MARK)

FROM EXAM_MARKS В

WHERE A.EXAM_DATE = В. EXAM_DATE);

Подзапрос вычисляет количество строк с одной и той же датой,

совпадающей с датой, для которой сформирована очередная группа

основного запроса.

Задание №1.

1. Напишите запрос с подзапросом для получения данных обо всех

оценках студента с фамилией «Иванов». Предположим, что его

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

2. Напишите запрос, выбирающий данные об именах всех студентов,

имеющих по предмету с идентификатором 101 балл выше общего среднего

балла.

3. Напишите запрос, который выполняет выборку имен всех студентов,

имеющих по предмету с идентификатором 102 балл ниже общего среднего

балла.

4. Напишите запрос, выполняющий вывод количества предметов, по

которым экзаменовался каждый студент, сдававший более 20 предметов.

5. Напишите команду SELECT, использующую связанные подзапросы

и выполняющую вывод имен и идентификаторов студентов, у которых

стипендия совпадает с максимальным значением стипендии для города, в

котором живет студент.

6. Напишите запрос, который позволяет вывести имена и

идентификаторы всех студентов, для которых точно известно, что они

проживают в городе, где нет ни одного университета.

7. Напишите два запроса, которые позволяют вывести имена и

идентификаторы всех студентов, для которых точно известно, что они

проживают не в том городе, где расположен их университет. Один запрос с

использованием соединения, а другой с использованием связанного

подзапроса.

Использование оператора EXISTS

Используемый оператор EXISTS (существует) генерирует значение

истина или ложь, подобно булеву выражению. Используя подзапросы в

качестве аргумента, этот оператор оценивает результат выполнения

подзапроса как истинный, если этот подзапрос генерирует выходные данные,

то есть в случае существования (возврата) хотя бы одного найденного

значения. В противном случае результат подзапроса ложный.

Оператор EXISTS не может принимать значение UNKNOWN (неизвестно).

Пусть, например, нужно извлечь из таблицы EXAM_MARKS данные о

студентах, получивших хотя бы одну неудовлетворительную оценку.

SELECT DISTINCT STUDENT_ID

FROM EXAM_MARKS A

WHERE EXISTS

(SELECT *

FROM EXAM_MARKS В

WHERE MARK < 3

AND B.STUDENT_ID = A. STUDENT_ID);

При использовании связанных подзапросов предложение EXISTS

анализирует каждую строку таблицы, на которую имеется ссылка во

внешнем запросе. Главный запрос получает строки-кандидаты на

проверку условия. Для каждой строки-кандидата выполняется

подзапрос. Как только подзапрос находит строку, где в столбце MARK

значение удовлетворяет условию, он прекращает выполнение и

возвращает значение истина внешнему запросу, который затем

анализирует свою строку-кандидата.

Например, требуется получить идентификаторы предметов

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

студентами:

SELECT DISTINCT SUBJ_ID

FROM EXAM_MARKS A

WHERE EXISTS

(SELECT *

FROM EXAM_MARKS В

WHERE A.SUBJ_ID = B.SUBJ_ID

AND A.STUDENT_ID < > B.STUDENT_ID);

Часто EXISTS применяется с оператором NOT (по-русски NOT EXISTS

интерпретируется, как «не существует»).

Если предыдущий запрос сформулировать следующим образом -

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

только одним студентом (другими словами, для которых не существует

другого сдававшего студента), то достаточно просто поставить NOT перед

EXISTS.

Следует иметь в виду, что в подзапросе, указываемом в операторе

EXISTS, нельзя использовать агрегирующие функции.

Возможности применения вложенных запросов весьма разнообразны.

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

каждого студента, сдавшего более одного предмета. SELECT *

FROM STUDENT FIRST

WHERE EXISTS

(SELECT SUBJ_ID

FROM EXAM_MARKS SECOND

GROUP BY SUBJ_ID

HAVING COUNT (SUBJ_ID) > 1

WHERE FIRST.STUDENT_ID = SECOND.STUDENT_ID);

Задание №2.

1. Напишите запрос с EXISTS, позволяющий вывести данные обо всех

студентах, обучающихся в вузах, которые имеют рейтинг выше 300.

2. Напишите предыдущий запрос, используя соединения.

3. Напишите запрос с EXISTS, выбирающий сведения обо всех

студентах, для которых в том же городе, где живет студент, существуют

университеты, в которых он не учится.

4. Напишите запрос, выбирающий из таблицы SUBJECT данные о

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

студентом.

ЛАБОРАТОРНАЯ РАБОТА № 4. ОПЕРАТОРЫ РЕДАКТИРОВАНИЯ

ДАННЫХ

MySQL позволяет использовать одни запросы внутри других запросов,

то есть вкладывать запросы друг в друга.

Предположим, известна фамилия студента («Петров»), но неизвестно

значение поля STUDENT_ID для него. Чтобы извлечь данные обо всех оценках

этого студента, можно записать следующий запрос:

SELECT * FROM EXAM_MARKS

WHERE STUDENT_ID =

(SELECT STUDENT_ID

FROM STUDENT SURNAME = 'Петров');

Как работает запрос со связанным подзапросом?

• Выбирается строка из таблицы, имя которой указано во внешнем

запросе.

• Выполняется подзапрос и полученное значение применяется для

анализа этой строки в условии предложения WHERE внешнего запроса.

• По результату оценки этого условия принимается решение о

включении или не включении строки в состав выходных данных.

• Процедура повторяется для следующей строки таблицы

внешнего запроса.

Следует обратить внимание, что приведенный выше запрос

корректен только в том случае, если в результате выполнения указанного в

скобках подзапроса возвращается единственное значение. Если в результате

выполнения подзапроса будет возвращено несколько значений, то этот

подзапрос будет ошибочным. В данном примере это произойдет, если в

таблице STUDENT будет несколько записей со значениями поля SURNAME =

'Петров'.

В некоторых случаях для гарантии получения единственного значения

в результате выполнения подзапроса используется DISTINCT. Одним из видов

функций, которые автоматически всегда выдают в результате единственное

значение для любого количества строк, являются агрегирующие функции.

Оператор IN также широко применяется в подзапросах. Он задает

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

истинности задаваемого этим оператором предиката.

Данные обо всех оценках (таблица EXAM_MARKS) студентов из

Воронежа можно выбрать с помощью следующего запроса:

SELECT *

FROM EXAM_MARKS

WHERE STUDENT_ID IN

(SELECT STUDENT_ID

FROM STUDENT

WHERE CITY = 'Воронеж');

Подзапросы можно применять внутри предложения HAVING. Пусть

требуется определить количество предметов обучения с оценкой,

превышающей среднее значение оценки студента с идентификатором 301:

SELECT COUNT (DISTINCT SUBJ_ID), MARK

FROM EXAM_MARKS

GROUP BY MARK

HAVING MARK >

(SELECT AVG(MARK)

FROM EXAM_MARKS

WHERE STUDENT_ID = 301);

Формирование связанных подзапросов

При использовании подзапросов во внутреннем запросе можно

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

запроса. В этом случае такой связанный подзапрос выполняется по одному

разу для каждой строки таблицы основного запроса.

Пример: выбрать сведения обо всех предметах обучения, по которым

проводился экзамен 20 января 1999 г.

SELECT *

FROM SUBJECT SU

WHERE '20/01/1999' IN

(SELECT EXAM_DATE

FROM EXAM_MARKS EX

WHERE SU.SUBJ_ID = EX.SUBJ_ID);

B некоторых СУБД для выполнения этого запроса может

потребоваться преобразование значения даты в символьный тип. В

приведенном запросе SU и EX являются псевдонимами (алиасами), то есть

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

данном запросе вместо настоящих имен.

В приведенном примере они используются вместо имен таблиц

SUBJECT И EXAM_MARKS.

Эту же задачу можно решить с помощью операции соединения таблиц:

SELECT DISTINCT SU.SUBJ_ID, SUBJ_NAME, HOUR, SEMESTER

FROM SUBJECT FIRST, EXAM_MARKS SECOND WHERE

FIRST.SUBJ_ID = SECOND.SUBJ_ID

AND SECOND. EXAM_DATE = '20/01/1999';

В этом выражении алиасами таблиц являются имена FIRST И SECOND.

Можно использовать подзапросы, связывающие таблицу со своей

собственной копией.

Например, надо найти идентификаторы, фамилии и стипендии студентов,

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

SELECT DISTINCT STUDENT_ID, SURNAME, STIPEND FROM

STUDENT El

WHERE STIPEND > (SELECT AVG (STIPEND) FROM STUDENT E2

WHERE El.KURS = E2.KURS);

Тот же результат можно получить с помощью следующего запроса:

SELECT DISTINCT STUDENT_ID, SURNAME, STIPEND FROM

STUDENT El,

(SELECT KURS, AVG (STIPEND) AS AVG_STIPEND FROM

STUDENT E2

GROUP BY E2.KURS) E3

WHERE El.STIPEND > AVG_STIPEND AND El.KURS=E3.KURS;

Обратите внимание - второй запрос будет выполнен гораздо быстрее.

Дело в том, что в первом варианте запроса агрегирующая функция AVG

выполняется над таблицей, указанной в подзапросе, для каждой строки

внешнего запроса.

В другом варианте вторая таблица (алиас Е2) обрабатывается

агрегирующей функцией один раз, в результате чего формируется

вспомогательная таблица (в запросе она имеет алиас ЕЗ), со строками

которой затем соединяются строки первой таблицы (алиас Е1). Следует

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

зависит от оптимизатора запросов конкретной СУБД.

Связанные подзапросы в HAVING

Ранее указывалось, что предложение GROUP BY позволяет

группировать выводимые SELECT-запросом записи по значению некоторого

поля.

Использование предложения HAVING позволяет при выводе

осуществлять фильтрацию таких групп. Предикат предложения HAVING

оценивается не для каждой строки результата, а для каждой группы

выходных записей, сформированной предложением GROUP BY внешнего

запроса.

Пусть, например, необходимо по данным из таблицы ЕXAM_MARKS

определить сумму полученных студентами оценок (значений поля MARK),

сгруппировав значения оценок по датам экзаменов и исключив те дни, когда

число студентов, сдававших в течение дня экзамены, было меньше 10.

SELECT EXAM_DATE, SUM (MARK)

FROM EXAM_MARKS A

GROUP BY EXAM_DATE

HAVING 10 <

(SELECT COUNT(MARK)

FROM EXAM_MARKS В

WHERE A.EXAM_DATE = В. EXAM_DATE);

Подзапрос вычисляет количество строк с одной и той же датой,

совпадающей с датой, для которой сформирована очередная группа

основного запроса.

Задание №1.

1. Напишите запрос с подзапросом для получения данных обо всех

оценках студента с фамилией «Иванов». Предположим, что его

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

2. Напишите запрос, выбирающий данные об именах всех студентов,

имеющих по предмету с идентификатором 101 балл выше общего среднего

балла.

3. Напишите запрос, который выполняет выборку имен всех студентов,

имеющих по предмету с идентификатором 102 балл ниже общего среднего

балла.

4. Напишите запрос, выполняющий вывод количества предметов, по

которым экзаменовался каждый студент, сдававший более 20 предметов.

5. Напишите команду SELECT, использующую связанные подзапросы

и выполняющую вывод имен и идентификаторов студентов, у которых

стипендия совпадает с максимальным значением стипендии для города, в

котором живет студент.

8. Напишите запрос, который позволяет вывести имена и

идентификаторы всех студентов, для которых точно известно, что они

проживают в городе, где нет ни одного университета.

9. Напишите два запроса, которые позволяют вывести имена и

идентификаторы всех студентов, для которых точно известно, что они

проживают не в том городе, где расположен их университет. Один запрос с

использованием соединения, а другой с использованием связанного

подзапроса.

Использование оператора EXISTS

Используемый оператор EXISTS (существует) генерирует значение

истина или ложь, подобно булеву выражению. Используя подзапросы в

качестве аргумента, этот оператор оценивает результат выполнения

подзапроса как истинный, если этот подзапрос генерирует выходные данные,

то есть в случае существования (возврата) хотя бы одного найденного

значения. В противном случае результат подзапроса ложный.

Оператор EXISTS не может принимать значение UNKNOWN (неизвестно).

Пусть, например, нужно извлечь из таблицы EXAM_MARKS данные о

студентах, получивших хотя бы одну неудовлетворительную оценку.

SELECT DISTINCT STUDENT_ID

FROM EXAM_MARKS A

WHERE EXISTS

(SELECT *

FROM EXAM_MARKS В

WHERE MARK < 3

AND B.STUDENT_ID = A. STUDENT_ID);

При использовании связанных подзапросов предложение EXISTS

анализирует каждую строку таблицы, на которую имеется ссылка во

внешнем запросе. Главный запрос получает строки-кандидаты на

проверку условия. Для каждой строки-кандидата выполняется

подзапрос. Как только подзапрос находит строку, где в столбце MARK

значение удовлетворяет условию, он прекращает выполнение и

возвращает значение истина внешнему запросу, который затем

анализирует свою строку-кандидата.

Например, требуется получить идентификаторы предметов

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

студентами:

SELECT DISTINCT SUBJ_ID

FROM EXAM_MARKS A

WHERE EXISTS

(SELECT *

FROM EXAM_MARKS В

WHERE A.SUBJ_ID = B.SUBJ_ID

AND A.STUDENT_ID < > B.STUDENT_ID);

Часто EXISTS применяется с оператором NOT (по-русски NOT EXISTS

интерпретируется, как «не существует»).

Если предыдущий запрос сформулировать следующим образом -

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

только одним студентом (другими словами, для которых не существует

другого сдававшего студента), то достаточно просто поставить NOT перед

EXISTS.

Следует иметь в виду, что в подзапросе, указываемом в операторе

EXISTS, нельзя использовать агрегирующие функции.

Возможности применения вложенных запросов весьма разнообразны.

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

каждого студента, сдавшего более одного предмета. SELECT *

FROM STUDENT FIRST

WHERE EXISTS

(SELECT SUBJ_ID

FROM EXAM_MARKS SECOND

GROUP BY SUBJ_ID

HAVING COUNT (SUBJ_ID) > 1

WHERE FIRST.STUDENT_ID = SECOND.STUDENT_ID);

Задание №2.

1. Напишите запрос с exists, позволяющий вывести данные обо всех

студентах, обучающихся в вузах, которые имеют рейтинг выше 300.

2. Напишите предыдущий запрос, используя соединения.

3. Напишите запрос с exists, выбирающий сведения обо всех студентах,

для которых в том же городе, где живет студент, существуют

университеты, в которых он не учится.

4. Напишите запрос, выбирающий из таблицы subject данные о

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

чем одним студентом.

ЛАБОРАТОРНАЯ РАБОТА 5. СОЗДАНИЕ БАЗЫ ДАННЫХ

Цель работы: научиться создавать базы данных в среде SQL Server

Management Studio.

Теоретические сведения

Родоначальником серии SQL Server и его основой является язык

запросов SQL. Данный язык был предложен сотрудником компании IBM

Эдгаром Коддом в начале 1970-х г.г. Изначально он назывался SEQUEL

(Structured English Query Language, структурированный английский язык для

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

переименован в SQL (Structured Query Language, структурированный язык

запросов). Официальным произношением стало [es kju:' el] — эс-кью-эл.

Несмотря на это, даже англоязычные специалисты по-прежнему часто

называют SQL сиквел, вместо эс-кью-эл (по-русски также часто говорят "эс-

ку-эль"). Целью разработки было создание простого непроцедурного языка,

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

навыков программирования. К началу 1980-х годов SQL завоевал

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

(СУБД) и привлек внимание Американского национального института по

стандартизации (American National Standards Institute, ANSI), который в 1986,

1989, 1992, 1999 и 2003 годах выпустил стандарты языка SQL. В 1989 году

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

стандартизации ISO (SQL:1989), а затем были приняты и опубликованы

стандарты SQL:1992, SQL:1999 и SQL:2003. В настоящее время все

производители распространенных реляционных СУБД поддерживают с

различной степенью соответствия стандарт SQL:2003. В основу языка SQL,

используемого в SQL Server, легла разновидность языка T-SQL (Transact-

SQL).

В начале 1980-х г.г. фирма IBM и в частности в то время ее

подразделениями Microsoft и Sybase была создана первая версия сетевой

СУБД, которая называлась SQL Server версия 1.0, для операционной системы

IBM OS/2. После этого под эту операционную систему было выпущено еще 3

версии SQL Server. В середине 1980-х г.г. компании Microsoft и Sybase

отделяются от фирмы IBM, и Microsoft начинает работу над своей

операционной системой Windows, и вместе с компанией Sybase продолжает

развитие SQL Server. В середине 1990-х г.г. Microsoft создала операционную

систему Windows NT и вместе с компанией Sybase выпустила первую версию

SQL Server для Windows версии 4.1.

В дальнейшем компания Sybase разорвала свои отношения с Microsoft,

и Microsoft сама продолжила работу над Microsoft SQL Server 6.0. Данная

версия была предназначена для работы в операционной системе Windows NT,

95 и 98. В 1999 г. выходит версия Microsoft SQL Server 7.0, которая стала

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

версия Micrsoft SQL Server 2000. В 2005 году появляется новая версия

сервера, основанная на новой технологии .NET, а в 2008 году – её

улучшенная версия – Microsoft SQL Server 2008.

В СУБД Microsoft SQL Server новую базу данных (БД) можно создать,

используя стандартные команды языка T-SQL.

Для создания новой БД с помощью T-SQL необходимо вначале перейти

в БД «Master». Это можно сделать либо выбором ее из выпадающего списка

баз данных на панели инструментов, либо набором команды USE Master

на вкладке нового запроса.

Все команды языка T-SQL набираются на вкладке нового запроса

(SQLQuery). Для того чтобы создать новый запрос на панели инструментов

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

T-SQL на панели инструментов необходимо нажать кнопку или

на вкладке нового запроса набрать команду GO.

В Microsoft SQL Server БД состоит из двух частей:

‒ файл данных – файл, имеющий расширение mdf и где находятся все

таблицы и запросы;

– файл журнала транзакций - файл, имеющий расширение ldf, содержит

журнал, где фиксируются все действия с БД. Данный файл предназначен для

восстановления БД в случае её выхода из строя.

Для создания нового файла данных используется SQL-команда

CREATE DATABASE, которая имеет следующий синтаксис:

CREATE DATABASE <Имя БД>

(Name=<Логическое имя>,

FileName=<Имя файла>

[Size=<Нач. размер>,]

[Maxsixe=<Макс. размер>,]

[FileGrowth=<Шаг>])

[LOG ON

(Name=<Логическое имя>,

FileName=<Имя файла>

[Size=<Нач. размер>,]

[Maxsixe=<Макс. размер >,]

[FileGrowth=<Шаг>])

Здесь Имя БД – имя создаваемой БД; Логическое имя –

определяет логическое имя файла данных БД, по которому происходит

обращение к файлу данных; Имя файла – определяет полный путь к файлу

данных; Нач. размер – начальный размер файла данных в МБ; Макс.

размер – максимальный размер файла данных в МБ; Шаг – шаг

увеличения файла данных, либо в МБ либо в %.

Параметры в разделе LOG ON аналогичны параметрам в разделе

CREATE DATABASE. Однако они определяют параметры журнала

транзакций.

Пример: Создать БД «Artworks», расположенную в файле

«D:\Artworks.mdf» и имеющую начальный размер файла данных – 3 МБ,

максимальный размер файла данных – 100 МБ и шаг увеличения файла

данных, равный 1 МБ. Файл журнала транзакций данной БД имеет имя

«ArtworksLog» и расположен в файле «D:\Artworks.ldf». Данный файл имеет

начальный размер, равный 1 МБ, максимальный размер, равный 20 МБ и шаг

увеличения, равный 1 МБ.

CREATE DATABASE Artworks

ON

(Name = Artworks,

FileName = ’D:\ Artworks.mdf’,

Size = 3MB,

Maxsixe = 100MB,

FileGrowth= 1MB)

LOG ON

(Name = Artworks Log,

FileName = ’D:\ Artworks.ldf’,

Size = 1MB,

Maxsixe = 20MB,

FileGrowth = 1MB)

В языке запросов T-SQL с БД возможны следующие действия:

1. Отображение сведений о БД: EXEC SP_HELPDB <Имя БД>;

2. Изменение параметров БД: EXEC SP_DBOPTION <Имя БД>,

<Параметр>, <Значение>;

3. Добавление новых файлов, удаление файлов и переименование

файлов, входящих в БД:

ALTER DATABASE <Имя БД>

ADD FILE (<Параметры>)|

REMOVE FILE <Логическое имя файла>|

MODIFY FILE (<Параметры>)

где, раздел ADD FILE – добавляет файл, REMOVE FILE – удаляет, а

раздел MODIFY FILE – изменяет параметры файла;

4. Сжатие всей БД: DBCC SHRINKDATABASE <Имя БД>;

5. Сжатие конкретного файла БД: DBCC SHRINKFILE <Логическое

имя файла>;

6. Переименование БД: EXEC SP_RENAMEDB <Имя БД>,<Новое

имя БД>;

7. Удаление БД: DROP DATABASE <Имя БД>.

Вышеперечисленные команды используют следующие параметры:

‒ <Имя БД> – имя БД с которой производится действие;

‒ <Параметр> – изменяемый параметр;

‒ <Значение> – новое значение изменяемого параметра;

‒ <Параметры> – параметры файла БД, аналогичные параметрам,

используемым в команде CREATE DATABASE;

‒ <Логическое имя файла> – логическое имя файла, входящего в

БД;

‒ <Новое имя БД> – новое имя БД.

Пример выполнения лабораторной работы

Для начала запустим среду разработки «SQL Server Management

Studio». Для этого в меню «Пуск» выбираем пункт «Все программы\Microsoft

SQL Server 2008\Среда SQL Server Management Studio» (рис. 1)

После запуска среды разработки появится окно подключения к серверу

«Соединение с сервером» (рис. 2).

В этом окне необходимо нажать кнопку «Соединить».

После нажатия кнопки «Соединить» появится окно среды разработки

«SQL Server Management Studio» (рис. 3).

Данное окно имеет следующую структуру:

1. Оконное меню – содержит полный набор команд для управления

сервером и выполнения различных операций.

2. Панель инструментов – содержит кнопки для выполнения наиболее

часто производимых операций. Внешний вид данной панели зависит от

выполняемой операции.

3. Панель «Обозреватель объектов» – обозреватель объектов.

Обозреватель объектов – это панель с древовидной структурой,

отображающая все объекты сервера, а также позволяющая производить

различные операции, как с самим сервером, так и с БД. Обозреватель

объектов является основным инструментом для разработки БД.

4. Рабочая область. В рабочей области производятся все действия с БД,

а также отображается её содержимое.

В обозревателе объектов сами объекты находятся в папках. Чтобы

открыть папку необходимо щёлкнуть по знаку «+» слева от изображения

папки.

Нажатие кнопки приводит к открытию окна запросов

(рис. 4).

Для выполнения запроса, введенного в этом окне, нужно нажать на

кнопку .

Выполним создание базы данных «Artworks» в среде разработки SQL

Server Management Studio. В новом окне запросов введем запрос на создание

БД:

IF DB_ID(‘Artworks’) IS NULL

CREATE DATABASE Artworks;

Если базы данных с именем Artworks не существует, приведенный

код создаст новую БД. Функция DB_ID принимает имя базы данных в

качестве входного параметра и возвращает внутренний ID базы данных. Если

БД с именем входного параметра не существует, функция возвращает

значение NULL. Это простой способ проверить наличие заданной БД.

В инструкции CREATE DATABASE используются установочные

параметры файла для задания его местоположения и начального размера.

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

Server Management Studio на панели обозревателя объектов в папке «Базы

данных» появится новая БД Artworks (рис. 5).

Вся информация в базе данных хранится в таблицах, которые

представляют собой средство хранения данных. Таблицы состоят из строк

или записей и столбцов или полей. Каждое поле имеет три характеристики:

1. Имя поля – используется для обращения к полю;

2. Значение поля – определяет информацию, хранимую в поле;

3. Тип данных поля – определяет, какой вид информации можно

хранить в поле.

В SQL сервер используется следующие типы данных:

Двоичные типы данных, которые содержат последовательности

нулей и единиц: 1) binary(n) – двоичный тип фиксированной длины размером

в n байт, где n — значение от 1 до 8000; размер при хранении составляет n

байт; 2) varbinary(n) – двоичный тип с переменной длиной, n может иметь

значение от 1 до 8000, max указывает, что максимальный размер при

хранении составляет 231-1 байт;

Целочисленные типы данных – типы данных для хранения целых

чисел (в скобках указан диапазон значений типа данных): tinyint (0..255),

smallint (-32768..+32767), int (-231..+(231–1)), bigint (-263..+(263–1));

Типы данных для хранения чисел с плавающей запятой: real

занимает в памяти 4 байта; float(n), где n — это количество битов,

используемых для хранения мантиссы числа в формате float при

экспоненциальном представлении, определяет точность данных и размер для

хранения; значение параметра n должно лежать в пределах от 1 до 53;

значением по умолчанию для параметра n является 53;

Типы данных для хранения чисел с фиксированными точностью и

масштабом: decimal(p, s) и numeric(p, s), где p (точность) – максимальное

количество десятичных разрядов числа (как слева, так и справа от

десятичной запятой). Точность должна быть значением в диапазоне от 1 до

38. По умолчанию это значение равно 18. s (масштаб) – максимальное

количество десятичных разрядов числа справа от десятичной запятой.

Масштаб может принимать значение от 0 до p и может быть указан только

совместно с точностью. По умолчанию масштаб принимает значение 0;

поэтому 0 ≤ s ≤ p;

Символьные типы данных: char(n) – cтроковые данные

фиксированной длины не в Юникоде, аргумент n определяет длину строки и

должен иметь значение от 1 до 8000, размер при хранении составляет n байт;

varchar(n | max) – строковые данные переменной длины не в Юникоде,

аргумент n определяет длину строки и должен иметь значение от 1 до 8000,

значение max указывает, что максимальный размер при хранении составляет

231–1 байт (2 ГБ); text – данные переменной длины не в Юникоде в кодовой

странице сервера и с максимальной длиной строки 231–1;

Специальные типы данных: bit – целочисленный тип данных,

который может принимать значения 1, 0 или NULL; image – тип данных для

хранения рисунка размером до 2ГБ;

Типы данных даты и времени: date (от 01.01.0001 до 31.12.9999);

datetime (диапазон даты – от 01.01.1753 до 31.12.1999, диапазон времени – от

00:00:00 до 23:59:590,997); smalldatetime (диапазон даты – от 01.01.1900 до

6.06.2079, диапазон времени – от 00:00:00 до 23:59:59); time (от

00:00:00.0000000 до 23:59:59.9999999);

Денежные типы данных для хранения финансовой информации:

money (8 байт) и smallmoney (4 байта) – типы данных, представляющие

денежные (валютные) значения.

Для создания таблиц в SQL Server в первую очередь необходимо

сделать активной ту БД, в которой создается таблица. Для этого в новом

запросе можно набрать команду: USE <Имя БД>, либо на панели

инструментов необходимо выбрать в выпадающем списке рабочую БД.

После выбора БД можно создавать таблицы.

Таблицы создаются командой

CREATE TABLE table_name

( { <column_definition> } )

[ ; ]

<column_definition> ::= column_name <data_type> [

NULL | NOT NULL ] [DEFAULT constant_expression ] | [

IDENTITY [ ( seed,increment ) ] ] [ <column_constraint>

[ ...n ] ]

<column_constraint> ::= [ CONSTRAINT

constraint_name ]

{ PRIMARY KEY | UNIQUE }

Здесь table_name – имя таблицы; column_name – имя столбца в

таблице; data_type – тип данных для столбца; IDENTITY указывает,

что новый столбец является столбцом идентификаторов, при этом seed –

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

increment – значение приращения, добавляемое к значению

идентификатора предыдущей загруженной строки; CONSTRAINT –

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

ограничения, constraint_name – имя ограничения; NULL | NOT NULL

определяет, допустимы ли для столбца значения NULL; PRIMARY KEY –

ограничение, которое определяет столбец первичным ключом таблицы.

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

скобки.

Пример: Создать таблицу «Artworks», содержащую поля:

а) код произведения (ArtworkId);

б) название произведения (Title);

в) жанр (Genre);

г) средства создания (Tools);

д) код автора (AuthorId);

е) дата создания (CreatDate);

ж) цена (Price);

з) отдел хранения (DepId).

SQL-запрос для создания этой таблицы имеет следующий вид:

USE Artworks;

IF OBJECT_ID(‘dbo.Artworks’, ‘U’) IS NOT NULL

DROP TABLE dbo.Artworks;

CREATE TABLE dbo.Artworks

(

ArtworkId BIGINT IDENTITY(1,1) CONSTRAINT

PK_Artworks PRIMARY KEY,

Title VARCHAR(100) NULL,

Genre VARCHAR (50) NULL,

Tools VARCHAR (50) NULL,

AuthorId BIGINT NULL,

CreatDate DATE NULL,

Price MONEY NULL,

DepId INT NOT NULL

);

Инструкция USE изменяет текущую связь с БД на связь с Artworks.

Включение этой инструкции в сценарии создания объектов очень важно, т.к.

гарантирует создание объектов в требуемой БД.

Инструкция IF запускает функцию OBJECT_ID, которая в качестве

входных параметров принимает имя объекта и его тип. Тип ‘U’

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

внутренний внутренний ID объекта, если объект с заданным именем и типом

уже существует, и значение NULL в противном случае.

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

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

умолчанию. Если опустить имя схемы при создании таблицы, то SQL Server

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

пользователя, выполняющего программный код.

Для каждого атрибута сущности Artworks задается его имя, тип и

допустимость значений NULL.

Для столбца ArtworkId определено ограничение в виде первичного

ключа (PK_Artworks), при этом значения ArtworkId будут начинаться с

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

(IDENTITY(1,1)).

Аналогично создаются и другие таблицы БД Artworks : Authors,

Employees, Departments. SQL-запросы для создания этих таблиц

приведены ниже.

USE Artworks;

IF OBJECT_ID(‘dbo.Authors’, ‘U’) IS NOT NULL

DROP TABLE dbo.Authors;

CREATE TABLE dbo.Authors

(

AuthorId BIGINT IDENTITY(1,1) CONSTRAINT

PK_Authors PRIMARY KEY,

Lastname VARCHAR(25) NOT NULL,

Firstname VARCHAR (25) NOT NULL,

Middlename VARCHAR (25) NULL,

DateOfBirth DATE NULL,

DateOfDeath DATE NULL,

Country VARCHAR(25) NULL

);

IF OBJECT_ID(‘dbo.Employees’, ‘U’) IS NOT NULL

DROP TABLE dbo.Employees;

CREATE TABLE dbo.Employees

(

EmpId BIGINT IDENTITY(1,1) CONSTRAINT

PK_Employees PRIMARY KEY,

Lastname VARCHAR(25) NOT NULL,

Firstname VARCHAR (25) NOT NULL,

Middlename VARCHAR (25) NOT NULL,

Position VARCHAR (25) NULL,

Salary MONEY NULL,

BeginDate DATE NOT NULL,

EndDate DATE NULL,

DepId INT NULL

);

IF OBJECT_ID(‘dbo.Departments’, ‘U’) IS NOT NULL

DROP TABLE dbo.Departments;

CREATE TABLE dbo.Departments

(

DepId INT IDENTITY(1,1) CONSTRAINT

PK_Departments PRIMARY KEY,

Name VARCHAR(25) NOT NULL

);

Для выполнения запросов, введенных в среде SQL Server Management

Studio, нужно нажать на кнопку .

Для того чтобы обеспечить ссылочную целостность в БД Artworks

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

Таблицы Artworks и Authors нужно связать по столбцу

AuthorId, а таблицы Artworks и Departments – по столбцу DepId.

Аналогично должны быть связаны между собой таблицы Employees и

Departments.

USE Artworks;

ALTER TABLE dbo.Artworks

ADD CONSTRAINT FK_Artw_Auth

FOREIGN KEY (AuthorId)

REFERENCES Authors (AuthorId);

ALTER TABLE dbo.Artworks

ADD CONSTRAINT FK_Artw_Dep

FOREIGN KEY (DepId)

REFERENCES Departments (DepId);

ALTER TABLE dbo.Employees

ADD CONSTRAINT FK_Emp_Dep

FOREIGN KEY (DepId)

REFERENCES Departments (DepId);

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

создать диаграмму, описывающую эти взаимосвязи.

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

элементе БД «Диаграммы баз данных» и в открывшемся контекстном меню

выбрать пункт «Создать диаграмму базы данных» (рис.6).

В открывшемся диалоговом окне нужно выбрать таблицы, которые

будут добавлены на диаграмму (рис. 7).

В результате будет получена диаграмма следующего вида:

Задание

1) Создать базу данных и ее таблицы в соответствии с вариантом.

Выполнить задание с помощью стандартных команд языка T-SQL.

2) Создать диаграмму БД средствами среды SQL Server Management

Studio.

ЛАБОРАТОРНАЯ РАБОТА 6. СВЯЗИ И ИНДЕКСЫ

Когда сервер SQL Server выполняет запрос, сначала требуется

определить наилучший способ выполнения. Для этого нужно рассчитать, как

и в каком порядке обращаться к данным и соединять их, как и когда

выполнять вычисления и агрегации и т. д. За это отвечает подсистема,

которая называется Query Optimizer(Оптимизатор запроса). Оптимизатор

запроса использует статистические данные о распределении

данных, метаданные, относящиеся к объектам в базе данных, информацию

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

выполнения запроса. Для каждого из этих планов Оптимизатор

запроса предполагает его стоимость на основе статистики по этим данным и

выбирает план с минимальными затратами ресурсов на выполнение.

Конечно, SQL Server не вычисляет всех возможных планов для каждого

запроса, поскольку для некоторых запросов сами эти вычисления могут

отнять больше времени, чем выполнение наименее эффективного из всех

планов. Следовательно, SQL Server использует сложные алгоритмы, чтобы

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

возможной. После того, как план выполнения сгенерирован, он хранится в

буферном кэше (на что SQL Server выделяет большую часть своей

виртуальной памяти). Затем план выполняется тем способом,

который Оптимизатор запроса сообщает ядру базы

данных (компоненту database engine).

Примечание. Планы выполнения в буферном кэше могут быть

повторно использованы при выполнении такого же или аналогичного

запроса. Следовательно, планы выполнения хранятся в кэше максимально

возможное время. Дополнительную информацию о кэшировании планов

выполнения см. в официальном документе под названием: "Batch

Compilation, Recompilation, and Plan Caching Issues in SQL Server 2005"

(Проблемы компиляции и рекомпиляции пакетов, а также кэширования

планов в SQL Server 2005) на странице http://www.microsoft.com/

technet/prodtechnol/sql/2005/recomp.mspx.

Сможет ли Query Optimizer (Оптимизатор запросов) сгенерировать

эффективный план для конкретного запроса, зависит от следующих аспектов:

Индексы. Подобно оглавлению в книге, индекс базы данных позволяет

быстро найти определенные строки в таблице. В таблице может быть

не один индекс. Благодаря наличию в таблице индексов, Оптимизатор

запросов SQL Server может оптимизировать доступ к данным, выбрав

для использования подходящий индекс. Если индексы отсутствуют, у

Оптимизатора запросов остается только один вариант, который

заключается в сканировании всех данных, имеющихся в таблице, в

поиске нужных строк. Далее в этой лекции приводится информация о

том, как работают индексы и как их разрабатывать и проектировать.

Статистика распределения данных:SQL Server хранит статистику о

распределении данных. Если эта статистика отсутствует или

устарела, Оптимизатор запросов не сможет вычислить эффективный

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

данные генерируются и обновляются автоматически. Далее в этой

лекции рассказывается о том, как генерируются статистические данные

и как можно управлять статистикой.

Как видите, генерирование плана выполнения запросов - это функция,

немаловажная для производительности SQL Server, поскольку эффективность

плана выполнения запроса определяет, будет ли время его выполнения

измеряться в миллисекундах, секундах или даже минутах. Планы

выполнения запросов, которые показали низкую скорость выполнения,

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

данные статистики или просто SQL Server выбрал не самый эффективный

план (такое случается не очень часто).

Примечание. Конечно, возможно, что неэффективно выполненный

запрос выполнялся в соответствии с хорошим планом. В этих случаях дело не

в оптимизации запроса. Скорее всего, проблема кроется совсем в другом,

например, в проекте запроса, конфликте доступа к данным, операций

ввода/вывода, памяти, использования ЦПУ, сетевых ресурсов и т. п. Чтобы

получить дополнительную информацию по этим проблемам, рекомендуем

ознакомиться с официальным документом "Troubleshooting Performance

Problems in SQL Server 2005" (Поиск и решение проблем с

производительностью в SQL Server 2005), который доступен по следующей

ссылке: http://www.microsoft.com/ technet/prodtechnol/sql/2005/tsprfprb.mspx.

Знакомимся с планами выполнения запросов

1. В меню Start (Пуск) выберите All Programs,. Microsoft SQL Server 2005,

SQL Server Management Studio (Все программы, Microsoft SQL Server

2005, Среда SQL Server Management Studio). Нажмите кнопку New

Query (Создать запрос), чтобы открыть окно нового запроса, и

измените контекст выполнения на базу данныхAdventure Works,

выбрав ее из раскрывающегося списка Available Databases (Доступные

базы данных).

2. Выполните следующую инструкцию SELECT. Код этого примера

имеется в файлах примеров под именем Viewing Query Plans.sql.

3. SELECT SalesOrderID, OrderQTY

4. FROM Sales.SalesOrderDetail

WHERE ProductID = 712 ORDER BY OrderQTY DESC

5. Чтобы вывести на экран план выполнения для этого запроса, нажмите

комбинацию клавиш (Ctrl+L) или выберите из меню Query (Запрос)

команду Display Estimated Execution Plan (Показать предполагаемый

план выполнения). План выполнения показан на следующем рисунке.

При генерировании предполагаемого плана запроса запрос на самом

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

Эта особенность Оптимизатора запросов является преимуществом, когда

приходится иметь дело с запросами, которые имеют

продолжительные рабочие циклы, ведь для того, чтобы увидеть план

выполнения запроса, нет необходимости выполнять сам запрос. Графическое

представление плана выполнения запроса читается справа налево и сверху

вниз. Каждый значок в плане представляет один оператор, а данные,

изменяемые между этими операторами, обозначены стрелками. Толщина

стрелок соответствует объему данных, которые передаются между

операторами. Мы не будем углубляться в подробности и объяснять значение

каждого оператора; расскажем только о тех из них, которые показаны в

данном плане выполнения запроса.

o SQL Server обращается к данным при помощи операции Clustered

Index Scan (Просмотр кластеризованного индекса). Это

сканирование представляет собой реальную операцию доступа к

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

o Данные переходят к оператору Sort (Сортировка), который

сортирует данные на основе предложения ORDER BY.

o Данные пересылаются клиенту.

Мы рассмотрим самые важные операторы, которые использует SQL

Server, когда будем изучать индексы и соединения. Полный список

операторов можно найти в Электронной документации SQL Server 2005, тема

"Пиктограммы графического представления плана выполнения".

Стоимость в процентах под пиктограммой каждого оператора

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

графической схеме. Это число поможет вам понять, какая операция

использует при выполнении больше всего ресурсов. В нашем случае самой

дорогостоящей операцией являетсяClustered Index

Scan (Просмотр кластеризованного индекса), которая составляет 89% общей

стоимости запроса.

6. Задержите указатель мыши над оператором Clustered Index

Scan (Просмотр кластеризованного индекса). Откроется окно с текстом

на желтом фоне, показанное на следующем рисунке.

В этом окне отображается подробная информация об операции. До сих

пор мы знали только то, что SQL Server извлекает данные при помощи

операции сканирования. Но в этом окне видно, что он выполняет

операцию Clustered Index Scan (Просмотр кластеризованного индекса)

(которая подробно рассматривается ниже) на кластеризованном

индексе таблицы Sales.SalesOrderDetail, а также поиск ProductID 712. Эта

информация находится в секции Predicates (Предикаты). Кроме того,

показаны предполагаемая стоимость и предполагаемое количество строк, а

также размер строки. В то время, как количество строк оценивается на основе

статистики, которую SQL Server хранит для этой таблицы, значения

стоимости вычисляются на основе статистики и значений эталонной

системы. Следовательно, значения стоимости не следует использовать для

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

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

дешевой или более дорогостоящей операции.

7. Эту информацию об операторах можно увидеть также в окне Properties

(Свойства) в SQL Server Management Studio. Чтобы открыть окно

Properties (Свойства), щелкните правой кнопкой мыши на значке

оператора и выберите из контекстного меню команду Properties

(Свойства).

8. Планы запросов можно также сохранить. Чтобы сохранить план

запроса, щелкните в панели плана правой кнопкой мыши и выберите из

контекстного меню команду Save Execution Plan As (Сохранить план

выполнения как). План сохраняется в формате XML с

расширением .sqlplan. Его можно открыть через SQL Server

Management Studio. выбрав из меню File (Файл) команды Open, File

(Открыть, Файл).

9. То, что вы видели до сих пор - это предполагаемый план выполнения

запроса, но можно просмотреть и действительный план выполнения.

Действительный план выполнения аналогичен предполагаемому плану

выполнения, но включает также действительные (не предполагаемые)

значения количества строк, количества перемоток и т. д. Чтобы

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

(Ctrl+M) или выберите из меню Query (Запрос) команду Include Actual

Execution Plan (Включить действительный план выполнения). Затем

нажмите F5 и выполните запрос. Результаты запроса отображаются как

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

вкладке Execution Plan (План выполнения).

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

запросов

Теперь мы рассмотрим, как работают индексы и как они

повышают производительность запросов. В SQL Server 2005 можно

определить два различных типа индексов: кластеризованные и

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

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

необходимо понимать, как данные и индексы хранятся в файлах данных и

как SQL Server осуществляет доступ к данным в файлах данных.

Структуры кучи

Файл данных в базе данных SQL Server делится на страницы по 8

Кбайт. Каждая страница содержит данные, индексы или другие типы данных,

которые нужны SQL Server, чтобы манипулировать файлами данных. Однако

большая часть страниц - это страницы данных или индекса. Страницы

представляют собой единицы, которые SQL Server считывает и записывает из

файлов данных. Каждая страница содержит данные или информацию индекса

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

данных вы найдете данные об одном объекте, а на каждой странице индекса -

только информацию индекса. В SQL Server 2000 невозможно разбить строки

данных на несколько страниц; это означает, что строка данных должна

уместиться на странице, что ограничивает размер строки примерно 8060 байт

(за исключением больших объектов данных). В SQL Server 2005 это

ограничение больше не существует для типов данных переменной длины,

таких, как nvarchar, varbinary, CLR и т. п. Благодаря типам данных

переменной длины строки могут занимать несколько страниц, но все строки с

типом данных фиксированной длины все же должны вписываться в одну

страницу.

Когда пользователь создает таблицу и вносит в нее данные, SQL Server

осуществляет поиск неиспользуемых страниц, на которых можно сохранить

данные. Чтобы отслеживать, какие страницы содержат данные для таблиц,

SQL Server для каждой таблицы хранит еще одну или больше

дополнительных страниц IAM, Index Allocation Map (Карта распределения

индекса). Эти IAM-страницы указывают на страницы, на которых хранятся

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

индекса, то есть, их объединяют только IAM-страницы, такие таблицы

называются кучами. Чтобы обратиться к данным в куче, SQL Server должен

прочитать IAM-страницу этой таблицы, а затем просмотреть страницы, на

которые ссылается IAM-страница. Эта операция называется просмотром, или

сканированием, таблицы. При просмотре таблицы данные считываются не по

порядку. Если запрос выполняет поиск какой-либо одной определенной

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

таблице только для того, чтобы найти нужную строку. Эта операция очень

неэффективна.

Изучаем структуры кучи

1. Запустите SQL Server Management Studio. Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. В следующем примере мы создадим две таблицы с

именами dbo.Orders и dbo.OrderDetails. Для создания таблиц и

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

Код для всего примера можно найти среди файлов примеров под

именем Examining Heap Structures.sql.

USE AdventureWorks;

GO

CREATE TABLE dbo.Orders( SalesOrderID int NOT NULL,

OrderDate datetime NOT NULL,

ShipDate datetime NULL,

Status tinyint NOT NULL,

PurchaseOrderNumber dbo.OrderNumber NULL,

CustomerID int NOT NULL,

ContactID int NOT NULL,

SalesPersonID int NULL

);

CREATE TABLE dbo.OrderDetails( SalesOrderID int NOT NULL,

SalesOrderDetailID int NOT NULL,

CarrierTrackingNumber nvarchar(25),

OrderQty smallint NOT NULL,

ProductID int NOT NULL,

UnitPrice money NOT NULL,

UnitPriceDiscount money NOT NULL,

LineTotal AS (isnull((UnitPrice*((1.0)-

UnitPriceDiscount))*OrderQty,(0.0)))

);

INSERT INTO dbo.Orders

SELECT SalesOrderID, OrderDate, ShipDate, Status, PurchaseOrderNumber,

CustomerID, ContactID, SalesPersonID

FROM Sales.SalesOrderHeader;

INSERT INTO dbo.OrderDetails(SalesOrderID, SalesOrderDetailID,

CarrierTrackingNumber, OrderQty,

ProductID, UnitPrice, UnitPriceDiscount)

SELECT SalesOrderID,

SalesOrderDetailID,CarrierTrackingNumber,OrderQty,

ProductID, UnitPrice, UnitPriceDiscount

FROM Sales.SalesOrderDetail;

3. Мы создали две таблицы со структурой хранения "куча". Для

выполнения запроса к таблице dbo.Orders введите инструкции, которые

приводятся ниже. Включите действительный план выполнения, нажав

(Ctrl+M) до начала выполнения или выбрав из меню Query (Запрос)

команду Include Actual Execution Plan (Включить действительный план

выполнения). Выполните запрос:

SET STATISTICS IO ON; SELECT * FROM dbo.Orders

SET STATISTICS IO OFF

Параметр SET STATISTICS IO включает функцию, которая вызывает

отправку сообщений о выполненных операциях дискового ввода/ вывода

обратно клиенту сервером SQL Server при выполнении инструкции. Это

замечательная функция, которую следует использовать для определения

стоимости операций ввода/вывода для запросов.

4. Перейдите на вкладку Messages (Сообщения). Вы увидите примерно

такое сообщение:

Этот вывод информирует, что SQL Server для данной операции должен

просмотреть данные таблицы один раз, причем нужно выполнить 178

считываний страниц (логических чтений). Этот вывод показывает также, что

физические считывания для выполнения этой операции не используются

(физические, или опережающие считывания). Физических считываний не

было потому, что, в данном случае, данные уже находились в буферном

кэше. Если окно Messages (Сообщения) показывает, что в данном запросе

выполнялись физические считывания, то выполните запрос еще раз; вы

увидите, что количество физических считываний будет меньше, чем было до

этого. Причина заключается в том, что SQL Server хранит страницы данных,

к которым недавно были обращения, в буферном кэше для повышения

производительности.

5. Перейдите на вкладку Execution Plan (План выполнения). В плане

выполнения, показанном на следующем рисунке, мы видим, что SQL

Server использовал операцию Table Scan (Просмотр таблицы) для

доступа к данным, как единственный возможный вариант.

6. Теперь немного изменим запрос, чтобы он возвратил указанные

строки.

SET STATISTICS IO ON;

SELECT * FROM dbo.Orders WHERE SalesOrderID =46699;

SET STATISTICS IO OFF;

7. Посмотрим вывод в виде сообщения и графическое представление

плана выполнения. Вы видите, что для этого запроса SQL Server все

еще требуется 178 считываний страниц и использование операции

Table Scan (Просмотр таблицы). Просмотр таблицы используется

потому, что SQL Server не располагает индексом, и, следовательно, ему

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

p>Мы видим, что SQL Server использует операции просмотра таблиц

для доступа к таблицам, не имеющим индекса. Эти просмотры

вынуждают SQL Server просматривать все данные независимо от

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

может потребоваться много времени.

Индексы в таблицах

Чтобы повысить производительность доступа к данным, следует

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

называется ключевым столбцом индекса. Индекс, встроенный в столбец,

похож на оглавление книги. Он включает отсортированные значения из этого

столбца и ссылки на страницы, на которых можно найти действительные

строки и данные.

Чтобы найти строки, в которых столбец индекса имеет определенное

значение, SQL Server должен будет найти в индексе это значение, а затем

перейти по указателю, чтобы прочитать строку. Эта операция намного проще

и дешевле, чем просмотр всех данных, который был выполнен нами при

выполнении просмотра таблицы.

Индексы в SQL Server встроены в древовидную структуру, которая

называется сбалансированным деревом. Основная структура

сбалансированного дерева показана ниже в примере. Как видите, нижний

уровень называется уровнем листовых вершин. Уровень листовых вершин

можно мыслить как оглавление книги. Он содержит по одной записи на

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

Чтобы ускорить поиск значений в индексе, дерево построено поверх него с

использованием операций сравнения < (меньше чем) и > (больше чем). Число

уровней индекса зависит от количества записей и размера ключа индекса. В

реальных условиях страница индекса могла бы содержать намного больше

значений, чем изображено в примере. Поскольку страница имеет размер 8

Кбайт, SQL Server может указать на странице индекса на тысячи страниц.

Следовательно, индекс обычно не имеет много уровней, даже если таблица

содержит миллионы строк. Этот факт способствует очень быстрому поиску

определенных значений.

Надписи:

Root (Level 2) - Уровень корневой вершины (2 уровень)

Intermediate (Level 1) - Уровень внутренних вершин (1 уровень)

Leaf Level (Level 0) - Уровень листовых вершин (0 уровень)

KEY Pointer - Ключ-указатель

Как уже отмечалось ранее, в SQL Server используется два типа

индексов: кластеризованный и некластеризованный. Оба типа индексов

представляют собой сбалансированные деревья, но построены они по-

разному. Давайте посмотрим, в чем заключается разница.

Кластеризованные индексы

Кластеризованные индексы представляют собой особый вид

сбалансированного дерева. Они отличаются от остальных индексов

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

листовых вершин не включает ключей индекса и указателей, вместо этого он

содержит сами данные. Это отличие означает, что данные уже не хранятся в

структуре кучи. Теперь они хранятся на уровне листовых вершин индекса и

отсортированы по ключу индекса. Такой проект имеет два преимущества:

Системе SQL Server для доступа к данным не нужно следовать по

указателю. Данные хранятся непосредственно в индексе.

Данные сортируются по ключу индекса, что является главным

преимуществом. Когда SQL Server потребуются данные,

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

операцию сортировки, потому что данные уже отсортированы.

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

определить только один кластеризованный индекс для каждой таблицы. Для

создания кластеризованного индекса используется следующая

синтаксическая конструкция:

CREATE [ UNIQUE ] CLUSTERED INDEX index name

ON <object> ( column [ ASC | DESC ] [ ,...n ] )

Как видите, можно определить индекс как уникальный, в этом случае

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

Примечание. SQL Server создает уникальный индекс, если для

таблицы определено ограничение первичного ключа, или уникальности.

Если определен первичный ключ, то он создает кластеризованный индекс по

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

индекса в SQL Server должен использоваться, если создание первичного, или

уникального, ограничения, может быть определено в

инструкциях CREATE или ALTER TABLE с ключевыми

словами CLUSTERED или NONCLUSTERED.

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

1. Запустите SQL Server Management Studio. Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. Для создания уникального кластеризованного индекса в

таблице Orders ведите и выполните следующую инструкцию. Код этого

примера имеется в файлах примеров под именем Creating And Using

Clustered Indexes.sql.

3. CREATE UNIQUE CLUSTERED INDEX CLIDX_Orders_SalesOrderID

ON dbo.Orders(SalesOrderID)

4. Теперь выполните эти же две инструкции SELECT, как делали это

раньше, и изучите различия. Обязательно включите при выполнении

запроса действительный план выполнения.

SET STATISTICS IO ON;

SELECT * FROM dbo.Orders;

SELECT * FROM dbo.Orders

WHERE SalesOrderID =46699;

SET STATISTICS IO OFF;

5. Перейдите на вкладку Execution Plan (План выполнения).

Мы видим, что SQL Server больше не использует просмотры таблицы.

Теперь он выполняет операции индексирования, потому что данные больше

не хранятся в структуре кучи. План выполнения запроса показывает, что в

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

возможных:

o Просмотр индекса.Просмотр уровня листовых вершин индекса

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

инструкция SELECT не имеет предложения WHERE, серверу

SQL Server известно, что нужно возвратить все данные, которые

хранятся на уровне листовых вершин индекса.

o Поиск по индексу.Операция, при которой SQL Server выполняет

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

начиная от корневой вершины индекса.

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

определенный диапазон данных. В этом виде операции частичного

просмотра SQL Server пытается найти начало диапазона, а затем

просматривает дерево до конца этого диапазона.

6. Перейдите на вкладку Messages (Сообщения), как показано ниже.

Мы видим, что первая инструкция SELECT генерирует почти тот же

объем считываний страниц, что и операция просмотра таблицы, если у нее

структура кучи. Это не удивительно, поскольку инструкция SELECT требует

все данные, и, следовательно, SQL Server должен возвратить все данные. Но

второй запрос генерирует только два считывания страниц, а это значительное

улучшение по сравнению с теми 178 считываниями страниц, которые были

показаны раньше. SQL Server необходимо только выполнить поиск по

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

чем поиск в каждой странице данных.

7. Введите следующую инструкцию SELECT, которая возвращает данные

в отсортированной форме, и нажмите (Ctrl+L), чтобы получить

предполагаемый план выполнения.

SELECT * FROM dbo.Orders ORDER BY SalesOrderID;

SELECT * FROM dbo.Orders ORDER BY OrderDate;

Из рисунка видно, что первая инструкция выполняет только

просмотр кластеризованного индекса и не сортирует данные. Причина

заключается в том, что эти данные уже отсортированы по SalesOrderID,

потому что этот столбец является ключом кластеризованного индекса.

Следовательно, серверу SQL Server для того, чтобы получить строки в

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

листовых вершин и возвратить результат.

Во втором запросе данные были отсортированы после получения.

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

место операция Sort (Сортировка), которая отсортировала данные по

столбцу OrderDate. Поскольку сортировка является очень дорогостоящей

операцией, второй запрос генерирует 93% общей стоимости обоих запросов.

Таким образом, имеет смысл определять кластеризованный индекс в

столбцах, которые часто используются в качестве аргумента сортировки или

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

чтобы SQL Server сначала сортировал данные в соответствии с критериями

группировки.

Теперь создадим составной индекс в таблице OrderDetails. Составной

индекс - это индекс, который определен более, чем для одного

столбца. Ключи индекса сортируются сначала по первому ключевому столбцу

индекса, затем по второму и т. д. Такой индекс полезен в тех случаях, когда

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

в запросах или когда уникальность строки определена через несколько

столбцов.

Создаем составной кластеризованный индекс

1. В SQL Server Management Studio введите и выполните следующие

инструкции, чтобы создать составной кластеризованный индекс в

таблице OrderDetails.

CREATE UNIQUE CLUSTERED INDEX CLIDX_OrderDetails

ON dbo.OrderDetails(SalesOrderID,SalesOrderDetailID)

2. Теперь введите еще две инструкции SELECT. Первая будет выполнять

поиск указанного значения SalesOrderID, а вторая -указанного

значения SalesOrderDetailID. Оба столбца являются индексными

столбцами нашего индекса CLIDX_OrderDetails. Нажмите (Ctrl+L),

чтобы отобразить предполагаемый план выполнения.

SELECT * FROM dbo.OrderDetails

WHERE SalesOrderID = 46999

SELECT * FROM dbo.OrderDetails

WHERE SalesOrderDetailID = 14147

Легко заметить, что в первом запросе, который выполняет поиск

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

строки использует поиск по индексам (метод seek). Во втором запросе он

использует просмотр индекса, который является более дорогостоящей

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

значение только во втором столбце составного индекса, ведь индекс

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

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

составной индекс следует использовать только тогда, когда поиск по

дополнительным столбцам выполняется исключительно в сочетании с

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

Некластеризованные индексы

В противоположность кластеризованным

индексам некластеризованные индексы не содержат всех строк данных на

уровне листовых вершин индекса. Вместо этого на уровне листовых вершин

хранятся все ключевые столбцы и указатели на строки в таблице.

Использование и запись указателей зависит от того, является ли базовая

таблица кучей или имеет кластеризованный индекс.

Куча.Если таблица не имеет кластеризованного индекса, SQL Server

хранит указатель в физической строке (идентификатор файла,

идентификатор страницы и идентификатор строки на странице) на

уровне листовых вершин некластеризованного индекса. Чтобы найти

определенную строку в этом случае, SQL Server выполняет поиск по

индексам (метод seek) и переходит по указателю, чтобы извлечь

строку.

Кластеризованный индекс.Если кластеризованный

индекс существует, SQL Server хранит ключи кластеризации индекса

строк как указатели на уровне листовых вершин некластеризованного

индекса. Если SQL Server возвращает строку

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

понекластеризованному индексу, возвращает соответствующий ключ

кластеризации, а затем выполняет поиск по кластеризованному

индексу, чтобы возвратить нужную строку.

Поскольку некластеризованные индексы не содержат полностью

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

Синтаксис для их создания во многом похож на синтаксис создания

кластеризованных индексов:

CREATE [ UNIQUE ] NONCLUSTERED INDEX index name

ON <object> ( column [ ASC | DESC ] [ ,...n ] )

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

1. Запустите SQL Server Management Studio. Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. Введите следующую инструкцию SELECT и нажмите клавиатурную

комбинацию (Ctrl+L), чтобы отобразить предполагаемый план

выполнения. Код этого примера имеется в файлах примеров под

именем Creating And Using Nonclustered Indexes.sql.

SELECT DISTINCT SalesOrderID, CarrierTrackingNumber

FROM dbo.OrderDetails

WHERE ProductID = 776

SQL Server выполняет операцию просмотра кластеризованного

индекса, потому что в таблице ProductID нет индекса. Чтобы ускорить

выполнение этого запроса, SQL Server потребуется индекс в

таблице ProductID. Поскольку кластеризованный индекс в

таблице OrderDetails уже определен, приходится

использовать некластеризованный индекс.

Примечание. Операция Sort (Сортировка) применяется в этом

плане выполнения запроса для получения неповторяющегося результата.

3. Чтобы создать некластеризованный индекс в столбце Product

ID таблицы OrderDetail, введите и выполните следующую инструкцию:

CREATE INDEX NCLIX_OrderDetails_ProductID

ON dbo.OrderDetails(ProductID)

4. Введите предыдущую инструкцию SELECT и нажмите клавиатурную

комбинацию (Ctrl+L), чтобы отобразить предполагаемый план

выполнения.

SELECT DISTINCT SalesOrderID, CarrierTrackingNumber

FROM dbo.OrderDetails

WHERE ProductID = 776

Если вы задержите указатель мыши над оператором IndexSeek, то

увидите, что SQL Server выполняет поиск по индексу (метод seek) в

таблице NCLIX_OrderDetails_ProductID, чтобы извлечь указатели на нужные

записи. Поскольку в этой таблице существует кластеризованный индекс, SQL

Server извлекает список ключей кластеризации в качестве указателей. Этот

список передается на вход оператора Nested Loops (Вложенные циклы),

который является разновидностью оператора Join (Соединение) (соединения

будут рассмотрены далее в этой лекции). Оператор Nested Loops использует

поиск (метод seek ) по кластеризованному индексу, чтобы возвратить нужные

строки данных, которые затем переходят к оператору Sort (Сортировка),

чтобы исключить повторяющиеся значения в результате. Так SQL Server

извлекает строки при помощи некластеризованного индекса, если

существует кластеризованный индекс.

5. Теперь посмотрим, как SQL Server осуществляет доступ к данным,

когда существует некластеризованный индекс в таблице, не

имеющей кластеризованного индекса. Введите и выполните

следующую инструкцию DROP INDEX, чтобы удалить из

таблицы OrderDetails кластеризованный индекс.

DROP INDEX OrderDetails.CLIDX_OrderDetails

6. Введите предыдущую инструкцию SELECT и нажмите клавиатурную

комбинацию (Ctrl+L), чтобы отобразить предполагаемый план

выполнения.

7. SELECT DISTINCT SalesOrderID, CarrierTrackingNumber

FROM dbo.OrderDetails

WHERE ProductID = 776

Мы видим, что в этом случае SQL Server использует оператор RID

Lookup, потому что указатели, которые SQL Server получил в результате

поиска по индексу (метод seek), представляют собой указатели на

физические строки данных, а не на ключи кластеризации. Оператор RID

Lookup - это оператор, который используется в SQL Server для извлечения

данных непосредственно со страницы.

8. Введите и выполните следующую инструкцию, чтобы снова

создать кластеризованный индекс.

CREATE UNIQUE CLUSTERED INDEX CLIDX_OrderDetails

ON dbo.OrderDetails(SalesOrderID,SalesOrderDetailID)

Использование покрывающих индексов

Не всегда нужно, чтобы при использовании некластеризованных

индексов SQL Server на втором этапе извлекал всю строку. Эта ситуация

возникает, когда некластеризованный индекс включает все данные таблицы,

которые нужно SQL Server для выполнения операции. Когда это происходит,

мы называем индекс покрывающим, потому что этот индекс покрывает весь

запрос. Покрывающие индексы могут существенно увеличить

производительность запроса; в этом легко убедиться на двух планах запросов

из предыдущего примера. В таких запросах стоимость операторов, которые

извлекают нужные строки данных, составляет 97% всей стоимости запроса.

Другими словами, запрос без этой операции будет выполнен в 32 раза

быстрее. Давайте рассмотрим, как работают покрывающие индексы.

Применяем покрывающие индексы

1. Запустите SQL Server Management Studio, Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. Идея покрывающих индексов заключается в том, что они содержат все

данные, необходимые для выполнения запросов. Если мы посмотрим

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

предыдущем примере, то увидим, что SQL Server нужны

столбцы SalesOrderID,CarrierTrackingNumber и ProductID.

Некластеризованный индекс NCLIX_OrderDetails_ProductID, который

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

этом столбце, а также столбец SalesOrderID, поскольку этот столбец является

ключевым столбцом кластеризованного индекса.

Поэтому SalesOrderIDявляется указателем, который SQL Server использует в

некластеризованном индексе. Следовательно, серверу SQL Server, чтобы

получить CarrierTrackingNumber, нужно возвратить строки данных, выполнив

поиск (метод seek) только по кластеризованному индексу. Во втором запросе

столбцаCarrierTrackingNumber нет в списке SELECT. Введите и выполните

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

разницу. Код этого примера имеется в файлах примеров под

именем Using Covered Indexes.sql.

SET STATISTICS IO ON

-не покрывающий

SELECT DISTINCT SalesOrderID, CarrierTrackingNumber

FROM dbo.OrderDetails

WHERE ProductID = 776

-покрывающий

SELECT DISTINCT SalesOrderID

FROM dbo.OrderDetails

WHERE ProductID = 776

SET STATISTICS IO OFF

На рисунке, показанном ниже, видно, что серверу SQL Server для

выполнения второго запроса нужно обратиться к кластеризованному

индексу, потому что индекс покрывает запрос, если

столбец CarrierTrackingNumber не выбран. Поскольку доступ к

кластеризованному индексу для каждой строки очень дорого стоит, второй

запрос составляет только 1% от общей стоимости пакета. Посмотрев на

вкладку Messages (Сообщения), мы видим, что для того,. чтобы покрыть

запрос, SQL Server нужно выполнить только 2 считывания страницы (вместо

709 считываний, необходимых для первого запроса).

Вы убедились, что покрывающий индекс может дать большой

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

запроса, если они нужны в результате. Но по общему правилу следует

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

сделать более вероятным применение покрывающего индекса. Не следует

использовать в инструкции SELECT символ звездочки (*) только потому, что

так проще составить запрос.

3. Предположим, что столбец CarrierTrackingNumber нужен в запросе, но

из соображений производительности следует использовать

покрывающий индекс. В SQL Server 2005 можно включить этот

столбец в некластеризованный индекс. Включенные столбцы

сохраняются в ключах индекса на уровне листовых

вершиннекластеризованного индекса, что устраняет необходимость

извлекать их из кластеризованного индекса. Чтобы включить

столбец CarrierTrackingNumber в некластеризованный индекс, введите

и выполните следующую инструкцию для удаления (DROP) индекса и

повторного создания ( CREATE ) индекса с включенным столбцом.

DROP INDEX dbo.OrderDetails.NCLIX_OrderDetails_ProductID

CREATE INDEX NCLIX_OrderDetails_ProductID

ON dbo.OrderDetails(ProductID)

INCLUDE (CarrierTrackingNumber)

4. Выполните первый неохваченный запрос, чтобы посмотреть, будет ли

он охвачен теперь.

SET STATISTICS IO ON

SELECT DISTINCT SalesOrderID, CarrierTrackingNumber

FROM dbo.OrderDetails

WHERE ProductID = 776

SET STATISTICS IO OFF

Взглянув на план выполнения, вы увидите, что доступ к

кластеризованному индексу больше не нужен. На вкладке сообщений видно,

что для выполнения этого запроса теперь требуется только 5 считываний

страниц, тогда как раньше требовалось 709.

Примечание. Включенные столбцы вызывают перегрузку сервера

SQL Server при изменении данных, потому что SQL Server приходится

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

больше места в файлах данных. Следовательно, включение столбцов

в некластеризованные индексы - это хороший способ повышения

производительности запросов, но не следует создавать индексы с

включением столбцов для всех запросов в приложении. Эту функцию

следует использовать исключительно для ускорения выполнения

проблемных запросов.

Индексы в вычисляемых столбцах

В таблице dbo.OrderDetails у нас есть вычисляемый столбец LineTotal,

который представляет значение LineTotal для строки и вычисляется по

следующей формуле:

(isnull(([UnitPrice]*((1.0)-[UnitPriceDiscount]))*[OrderQty],(0.0)))

При каждом доступе к этому столбцу SQL Server вычисляет значения

на основе значений оригинальных строк, на которые ссылается формула.

Этот процесс не представляет собой проблемы до тех пор, пока

столбец LineTotal используется в предложении SELECT, но если

использовать LineTotal в предикате поиска предложения WHERE или в

агрегатных функциях, например, MAX или MIN, такая проблема может

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

Server должен вычислять значения для каждой строки в таблице, и только

потом искать в результатах нужные строки. Это очень неэффективный

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

просмотр кластеризованного индекса. Для этих видов запросов можно

создать индексы в вычисляемых столбцах. Когда в вычисляемом столбце

создан индекс, SQL Server вычисляет результат заранее и создает по нему

индекс.

Примечание. Существуют некоторые ограничения на создание

индексов в вычисляемых столбцах. Главное из них заключается в том,

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

прецизионными. Дополнительную информацию об этих ограничениях можно

найти в Электронной документации SQL Server 2005 в теме "Создание

индексов в вычисляемых столбцах".

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

1. Запустите SQL Server Management Studio. Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. Представьте себе, что вы хотите возвратить все

значения SalesOrderID s, для которых LineTotal имеет определенную

величину. Введите следующий запрос и нажмите (Ctrl+L), чтобы

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

вычисляемом столбце не существует опорного индекса. Как видите,

SQL Server должен выполнить просмотр кластеризованного индекса,

вычислить значения в столбце и отфильтровать эти значения до

нужных строк. Код этого примера можно найти среди файлов примеров

под именем IndexesOnComputedColumns.sql.

SELECT SalesOrderID

FROM OrderDetails

WHERE LineTotal = 27893.619

3. Введите и выполните следующую инструкцию CREATE INDEX, чтобы

создать индекс в вычисляемом столбце:

CREATE NONCLUSTERED INDEX NCL_OrderDetail_LineTotal ON

dbo.OrderDetails(LineTotal)

4. Выделите первый запрос и нажмите (Ctrl+L), чтобы отобразить новый

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

показано ниже, SQL Server теперь использует вновь созданный индекс

для извлечения данных, что повышает скорость выполнения запроса по

сравнению с предыдущим случаем.

SELECT SalesOrderID

FROM OrderDetails

WHERE LineTotal = 27893.619

5. Закройте окно среды SQL Server Management Studio.

Индексы в представлениях

Представлению без индекса не требуется место для хранения; когда

оно используется в инструкции, SQL Server объединяет описание

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

запроса и возвращает данные. Издержки этого процесса могут быть

значительными, если представление обрабатывает или объединяет много

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

представление, если к нему часто выполняются запросы.

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

файле данных, как это делается для кластеризованной таблицы. SQL Server

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

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

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

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

таблице. Следовательно, можно предусмотреть использование индексации

представлений, если представление обрабатывает много строк, как при

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

не слишком часто изменяются.

В SQL Server 2005 Enterprise, Developer или Evaluation Edition

индексированные представления могут ускорить выполнение запросов,

которые не ссылаются на представления напрямую. Если обрабатываемый

запрос включает, например, агрегат, и Оптимизатор запросов SQL Server

обнаруживает индексированное представление, в которое этот агрегат уже

включен, он запросит агрегат из индекса, а не станет вычислять его.

Чтобы создать индексированное представление, выполните следующие

действия:

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

1. Создайте представление при помощи предложения SCHEMABINDING.

Это представление должно удовлетворять нескольким требованиям.

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

существуют в одной базе данных. Все ссылочные функции должны

быть детерминированными; функции наборов, производные таблицы и

подзапросы не допустимы. Полный список требований можно найти в

теме "Создание индексированных представлений" Электронной

документации SQL Server 2005.

2. Создайте в этом представлении уникальный кластеризованный индекс.

Уровень листовых вершин этого индекса состоит из полного

результирующего набора представления.

3. При необходимости поверх кластеризованного индекса создайте не-

кластеризованные индексы. Некластеризованные индексы можно

создавать как обычно.

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

1. Запустите SQL Server Management Studio. Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. Введите и выполните следующую инструкцию, чтобы создать

представление, которое агрегирует итог LineTotal, сгруппированный по

месяцам заказа. Код этого примера можно найти среди файлов

примеров под именем CreatingAndUsingIndexedViews.sql.

CREATE VIEW dbo.vOrderDetails

WITH SCHEMABINDING AS

SELECT DATEPART(yy,Orderdate) as Year,

DATEPART(mm,Orderdate) as Month,

SUM(LineTotal) as OrderTotal,

COUNT_BIG(*) as LineCount

FROM dbo.Orders o INNER JOIN dbo.OrderDetails od

ON o.SalesOrderID = od.SalesOrderID

GROUP BY DATEPART(yy,Orderdate),

DATEPART(mm,Orderdate)

3. Введите и выполните следующую инструкцию SELECT. На вкладке

Messages (Сообщения) видно, что для выполнения этой инструкции

SQL Server требуется почти 1000 считываний страниц.

SET STATISTICS IO ON

SELECT Year, Month, OrderTotal

FROM dbo.OrderDetails

ORDER BY Year, Month

SET STATISTICS IO OFF

4. Введите и выполните следующую инструкцию CREATE INDEX, чтобы

создать уникальный кластеризованный индекс в

представлении vOrderDetails.

CREATE UNIQUE CLUSTERED INDEX

CLIDX_vOrderDetails_Year_Month

ON dbo.vOrderDetails(Year,Month)

5. Выполните еще одну инструкцию SELECT. Обратите внимание на то,

что SQL Server требуется только два считывания страниц, потому что

результат уже вычислен и хранится в индексе.

SET STATISTICS IO ON

SELECT Year, Month, OrderTotal

FROM dbo.OrderDetails

ORDER BY Year, Month

SET STATISTICS IO OFF

6. Если у вас установлена одна из следующих версий пакета: SQL Server

2005 Enterprise, Developer или Evaluation edtition - введите и выполните

следующую инструкцию SELECT, которая не ссылается на пред

ставление, и нажмите (Ctrl+L), чтобы запросить предполагаемый план

выполнения, как показано ниже.

SELECT DATEPART(yy,Orderdate) as Year,

SUM(LineTotal) as YearTotal

FROM dbo.Orders o INNER JOIN dbo.OrderDetails od

ON o.SalesOrderID = od.SalesOrderID

GROUP BY DATEPART(yy,Orderdate)

7. План выполнения предыдущего запроса показывает, что SQL Server

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

данные, поскольку гораздо эффективнее создать агрегат YearTotal,

вычислив сумму найденных в представлении агрегатов за месяц. Таким

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

становятся возможным ускорить запрос, не изменяя код самого

запроса.

8. Закройте окно среды SQL Server Management Studio.

Индексы, ускоряющие операции соединения

Операторы соединения используются для соединения таблиц или

промежуточных результатов. В SQL Server используется три типа операторов

соединения.

Соединения вложенных циклов (Nested Loop Join) используют один

ввод соединения в качестве внутренней входной таблицы, а другой

ввод соединения в качестве внешней входной таблицы. Вложенные

циклы однократно сканируют каждую строку ввода и ищут

соответствующие строки во внешнем вводе. Если в столбцах условий

соединения внешнего ввода существуют индексы, SQL Server может

использовать поиск по индексу (метод seek) для отыскания строк во

внешнем вводе. Если индексов не существует, то SQL Server

приходится использовать операторы сканирования, чтобы найти во

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

ввода. Вложенные циклы всегда используются в тех случаях, когда

внутренний ввод имеет всего несколько строк, поскольку в этом

случае, это самая эффективная операция соединения.

Соединение слиянием (Merge Join) используется, когда вводы

соединения сортируются по своим столбцам соединения. Выполняя

операцию соединения слиянием, SQL Server сканирует однократно

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

закрывают замок-молнию. Операция соединения слиянием очень

эффективна, но сортировка данных должна быть выполнена заранее, а

это означает, что в соединяемых столбцах должны существовать

индексы. Если индексы не существуют, SQL Server может принять

решение сначала выполнить сортировку ввода, но это не следует

делать очень часто, поскольку сортировка данных - обычно

неэффективный процесс.

Хэшированное соединение (Hash Join) используется для больших не-

индексированных вводов без сортировки. Хэшированное соединение

использует для соединения вводов операции хэширования в

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

операции хэширования, SQL Server требуется больше времени и

ресурсов процессора, чем для других операций соединения.

Изучаем операции соединения

1. Запустите SQL Server Management Studio. Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. Введите и выполните следующую инструкцию, не забудьте включить

действительный план выполнения запроса. Код этого примера можно

найти в файлах примеров под именем Examining Join Operations.sql.

План выполнения этой инструкции показан ниже. Мы видим, что SQL

Server для того, чтобы извлечь строки внутреннего ввода (данные из

таблицы OrderDetails ), использует поиск по индексу (метод seek ), а

затем оператор Nested Loop Join, потому что во внешнем вводе есть

только одна совпадающая строка (данные из таблицы Orders ).

Совпадающие строки внешнего ввода возвращаются также с помощью

поиска по индексу (метод seek ), поскольку существует совпадающий

индекс. Для извлечения данных SQL Server требуется только пять

считываний страниц, как указано на вкладке Messages (Сообщения).

SET STATISTICS IO ON

SELECT o.SalesOrderID, o.OrderDate, od.ProductID

FROM dbo.Orders o INNER JOIN dbo.OrderDetails od

ON o.SalesOrderID = od.SalesOrderID

WHERE o.SalesOrderID = 43659

3. Измените запрос так, чтобы требовалось извлечь более одного

значения SalesOrderID. В этом случае SQL Server выполнит соединение

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

внутреннем вводе много строк. Введите и выполните следующий

запрос: Вы увидите, что для выполнения этого запроса SQL Server

требуется 19 считываний страниц.

SELECT o.SalesOrderID, o.OrderDate, od.ProductID

FROM dbo.Orders o

INNER JOIN dbo.OrderDetails od

ON o.SalesOrderID = od.SalesOrderID

WHERE o.SalesOrderID BETWEEN 43659 AND 44000

4. Введите и выполните следующий пакет, который удаляет опорные

индексы.

DROP INDEX CLIDX_Orders_SalesOrderID ON dbo.Orders

DROP INDEX CLIDX_OrderDetails ON dbo.OrderDetails

5. Снова выполните инструкцию SELECT, которую мы использовали

ранее (она снова показана ниже) и изучите изменения планов

выполнения запросов и операций ввода/вывода.

SELECT o.SalesOrderID, o.OrderDate, od.ProductID

FROM dbo.Orders o INNER JOIN dbo.OrderDetails od

ON o.SalesOrderID = od.SalesOrderID

WHERE o.SalesOrderID = 43659

SELECT o.SalesOrderID, o.OrderDate, od.ProductID

FROM dbo.Orders o INNER JOIN dbo.OrderDetails od

ON o.SalesOrderID = od.SalesOrderID

WHERE o.SalesOrderID BETWEEN 43659 AND 44000

План выполнения предыдущего запроса показывает, что SQL Server

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

что внутренний ввод небольшой, и хэшированное соединение для второго

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

опорных индексов не существует, SQL Server приходится сканировать в

обоих случаях базовые таблицы полностью, что требует более 1000

считываний страниц для каждого запроса.

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

иметь опорные индексы. По общему правилу все ограничения по внешнему

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

соединения почти для всех запросов.

Распределение данных и статистика

В последнем примере мы видели, что SQL Sever выбирает

разные операторы соединения, исходя из размера ввода для соединения.

Кроме того, для других операций, например, для поиска по индексу (метод

seek) или сканирования, SQL Server также нужно знать, сколько строк будет

использоваться, чтобы определить, какой оператор лучше использовать.

Такой оператор определяется на основе статистических данных, поскольку

SQL Server должен сделать это до действительного доступа к данным. Эти

статистические данные создаются и обновляются SQL Server автоматически

по столбцам при помощи следующих шагов:

1. Запрос передается SQL Server.

2. Запускается Оптимизатор запросов SQL Server, который определяет, к

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

3. SQL Server выполняет поиск статистических данных в столбце, к

которому происходит обращение.

4. Если статистика уже существует и не является устаревшей, SQL Server

может продолжать выполнение запроса.

5. Если статистика не существует, SQL Server генерирует новые

статистические данные.

6. Если статистика существует, но является устаревшей, SQL Server

вычисляет новые статистические данные для этих данных.

7. Оптимизатор запросов SQL Server продолжает работу и генерирует

план выполнения запроса.

Таково поведение по умолчанию, но для большинства баз данных

существует лучший вариант. Можно воспользоваться инструкцией ALTER

DATABASE, чтобы информировать SQL Server о том, что нужно обновлять

данные статистики асинхронно; это означает, что программа не будет ждать

новых статистических данных при генерации плана выполнения запроса.

Безусловно, это означает, что сгенерированный план выполнения запроса

может не быть оптимальным, поскольку при его создании были

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

быть желательным сгенерировать или обновить данные статистики вручную.

Это можно сделать с помощью инструкций CREATE

STATISTICS или UPDATE STATISTICS. Можно также отключить

автоматическое создание и обновление индекса на уровне базы данных,

выполнив инструкцию ALTER DATABASE. Все эти варианты следует

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

умолчанию прекрасно подходит для большинства случаев. Дополнительную

информацию об этих вариантах можно прочитать в официальном документе

"Statistics Used by the Query Optimizer in Microsoft SQL Server 2005"

(Статистические данные, используемые Оптимизатором запросов в

Microsoft SQL Server 2005), который можно найти по

ссылке http://www.microsoft.com/technet/prodtechnol/sql/ 2005/qrystats.mspx.

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

1. Запустите SQL Server Management Studio. Откройте окно New Query

(Создать запрос) и измените контекст базы данных на Adventure Works.

2. Чтобы получить информацию о существующей статистике, можно

выполнить запросы к представлениям sys.stats и sys.stats_columns.

Введите и выполните следующий запрос, чтобы узнать, какие

статистические данные существуют для таблицы dbo:OrderDetails. Код этого

примера можно найти среди файлов примеров под

именем DataDistributionStatistics.sql.

SELECT s.NAME, COL_NAME ( s.object_ID, sc.column_id) as CNAME

FROM sys.stats s INNER JOIN sys.stats_columns sc

ON s.stats_id = sc.stats_id

AND s.object_id = sc.object_id

WHERE s.object_id = OBJECT_ID('dbo.OrderDetails')

ORDER BY s.NAME;"

Результат сообщает нам имена статистических показателей и столбцов,

для которых они рассчитаны. Статистические данные для индексированных

столбцов получают названия по названию соответствующего индекса.

Статистические данные с именами, которые начинаются с WA_Sys - это

статистика, которую SQL Server создает автоматически для столбцов, не

имеющих индекса.

1. Чтобы получить статистическую информацию, можно использовать

инструкцию DBCC SHOW_STATISTICS. Чтобы отобразить статистику

для столбца LineTotal таблицы dbo.OrderDetails, введите и выполните

следующую инструкцию:

DBCC SHOW_STATISTICS('dbo.OrderDetails', 'LineTotal')

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

инструкции DBCCSHOW_ STATISTICS. Первая часть - это общая

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

количество строк в выборке. Отображается также информация о плотности

данных. Плотность - это величина, показывающая, сколько

неповторяющихся значений в анализируемом столбце. Помимо этой общей

информации SQL Server определяет диапазоны в данных, которые

называются шагами, и сохраняет статистику распределения для этих шагов.

С помощью этой статистики распределения SQL Server может, используя

статистическую информацию для диапазона, к которому принадлежит

указанное значение, предположить, сколько строк будет вовлечено в поиск

указанного значения. Для каждого шага SQL Server хранит следующую

информацию:

RANGE_HI_KEY - значение верхней границы шага.

EQ_ROWS - количество строк, которое равно значению

the RANGE_HI_KEY.

RANGE_ROWS - количество строк в диапазоне, не считая границ.

DISTINCT_RANGE_ROWS - количество неповторяющихся значений

внутри диапазона.

AVG_RANGE_ROWS - среднее количество строк в расчете на каждое

различимое значение ключа.

Закройте окно среды SQL Server Management Studio.

Задание:

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

необходимые индексы для своего варианта.

ЛАБОРАТОРНАЯ РАБОТА 7. КЛАССИФИКАТОРЫ

В этом разделе описывается создание и проверка определяемой

пользователем функции-классификатора (UDF). Шаги включают выполнение

инструкций языка Transact-SQL в редакторе запросов Среда SQL Server

Management Studio.

Пример, показанный в следующей процедуре, иллюстрирует

возможности создания довольно сложной определяемой пользователем

функции-классификатора.

Пример.

Пул ресурсов (pProductionProcessing) и группа рабочей нагрузки

(gProductionProcessing) создаются для рабочей обработки в течение

определенного диапазона времени.

Пул ресурсов (pOffHoursProcessing) и группа рабочей нагрузки

(gOffHoursProcessing) создаются для управления соединениями, не

удовлетворяющими требованиям рабочей обработки.

Таблица (TblClassificationTimeTable) создается в базе данных master

для сохранения времени запуска и остановки, которые можно

вычислить относительно времени входа в систему. Она должна быть

создана в базе данных master, так как регулятор ресурсов использует

для функций-классификаторов привязку к схеме.

Функция-классификатор увеличивает время входа в систему. Излишне

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

снизить скорость быстрых соединений.

Создание определяемой пользователем функции-классификатора

1. Создайте и настройте новые пулы ресурсов и группы рабочей

нагрузки. Назначьте каждую группу рабочей нагрузки

соответствующему пулу ресурсов.

--- Create a resource pool for production processing

--- and set limits.

USE master

GO

CREATE RESOURCE POOL pProductionProcessing

WITH

(

MAX_CPU_PERCENT = 100,

MIN_CPU_PERCENT = 50

)

GO

--- Create a workload group for production processing

--- and configure the relative importance.

CREATE WORKLOAD GROUP gProductionProcessing

WITH

(

IMPORTANCE = MEDIUM

)

--- Assign the workload group to the production processing

--- resource pool.

USING pProductionProcessing

GO

--- Create a resource pool for off-hours processing

--- and set limits.

CREATE RESOURCE POOL pOffHoursProcessing

WITH

(

MAX_CPU_PERCENT = 50,

MIN_CPU_PERCENT = 0

)

GO

--- Create a workload group for off-hours processing

--- and configure the relative importance.

CREATE WORKLOAD GROUP gOffHoursProcessing

WITH

(

IMPORTANCE = LOW

)

--- Assign the workload group to the off-hours processing

--- resource pool.

USING pOffHoursProcessing

GO

Обновите конфигурацию, хранимую в памяти.

ALTER RESOURCE GOVERNOR RECONFIGURE

GO

Создайте таблицу и определите время запуска и остановки диапазона

времени рабочей обработки.

USE master

GO

CREATE TABLE tblClassificationTimeTable

(

strGroupName sysname not null,

tStartTime time not null,

tEndTime time not null

)

GO

--- Add time values that the classifier will use to

--- determine the workload group for a session.

INSERT into tblClassificationTimeTable VALUES('gProductionProcessing',

'6:35 AM', '6:15 PM')

go

2. Создайте функцию-классификатор, использующую функции и

значения времени, которые можно оценить со временем в таблице

подстановки. Дополнительные сведения об использовании таблиц

подстановки в функции-классификаторе см. в подразделе

«Рекомендации по использованию таблиц подстановки в функции-

классификаторе» данного раздела.

CREATE FUNCTION fnTimeClassifier()

RETURNS sysname

WITH SCHEMABINDING

AS

BEGIN

DECLARE @strGroup sysname

DECLARE @loginTime time

SET @loginTime = CONVERT(time,GETDATE())

SELECT TOP 1 @strGroup = strGroupName

FROM dbo.tblClassificationTimeTable

WHERE tStartTime <= @loginTime and tEndTime >= @loginTime

IF(@strGroup is not null)

BEGIN

RETURN @strGroup

END

--- Use the default workload group if there is no match

--- on the lookup.

RETURN N'gOffHoursProcessing'

END

GO

3. Зарегистрируйте функцию-классификатор и обновите конфигурацию,

хранимую в памяти.

ALTER RESOURCE GOVERNOR with (CLASSIFIER_FUNCTION =

dbo.fnTimeClassifier)

ALTER RESOURCE GOVERNOR RECONFIGURE

GO

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

пользователем функции-классификатора

1. Получите пул ресурсов и настройку группы рабочей нагрузки при

помощи следующего запроса.

USE master

SELECT * FROM sys.resource_governor_resource_pools

SELECT * FROM sys.resource_governor_workload_groups

GO

2. С помощью следующих запросов убедитесь в том, что функция-

классификатор существует и включена.

--- Get the classifier function Id and state (enabled).

SELECT * FROM sys.resource_governor_configuration

GO

--- Get the classifer function name and the name of the schema

--- that it is bound to.

SELECT

object_schema_name(classifier_function_id) AS [schema_name],

object_name(classifier_function_id) AS [function_name]

FROM sys.dm_resource_governor_configuration

3. Получите текущие данные среды выполнения пулов ресурсов и групп

рабочей нагрузки с помощью следующего запроса.

SELECT * FROM sys.dm_resource_governor_resource_pools

SELECT * FROM sys.dm_resource_governor_workload_groups

GO

4. С помощью следующего запроса выясните, какие сеансы существуют в

каждой группе.

SELECT s.group_id, CAST(g.name as nvarchar(20)), s.session_id,

s.login_time, CAST(s.host_name as nvarchar(20)), CAST(s.program_name AS

nvarchar(20))

FROM sys.dm_exec_sessions s

INNER JOIN sys.dm_resource_governor_workload_groups g

ON g.group_id = s.group_id

ORDER BY g.name

GO

5. С помощью следующего запроса выясните, какие запросы существуют

в каждой группе.

SELECT r.group_id, g.name, r.status, r.session_id, r.request_id, r.start_time,

r.command, r.sql_handle, t.text

FROM sys.dm_exec_requests r

INNER JOIN sys.dm_resource_governor_workload_groups g

ON g.group_id = r.group_id

CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS t

ORDER BY g.name

GO

6. С помощью следующего запроса выясните, какие запросы

выполняются в классификаторе.

SELECT s.group_id, g.name, s.session_id, s.login_time, s.host_name,

s.program_name

FROM sys.dm_exec_sessions s

INNER JOIN sys.dm_resource_governor_workload_groups g

ON g.group_id = s.group_id

AND 'preconnect' = s.status

ORDER BY g.name

GO

SELECT r.group_id, g.name, r.status, r.session_id, r.request_id, r.start_time,

r.command, r.sql_handle, t.text

FROM sys.dm_exec_requests r

INNER JOIN sys.dm_resource_governor_workload_groups g

ON g.group_id = r.group_id

AND 'preconnect' = r.status

CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS t

ORDER BY g.name

GO

Рекомендации по использованию таблиц подстановки в функции-

классификаторе

1. Не используйте таблицу подстановки без крайней необходимости. Если

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

саму функцию, однако при этом следует учитывать сложность и

динамические изменения функции-классификатора.

2. Ограничьте ввод и вывод данных, выполняемые для таблиц

подстановки.

a. Воспользуйтесь инструкцией TOP 1, чтобы вернуть только одну

строку.

b. Сведите к минимуму количество строк в таблице.

c. Сделайте так, чтобы все строки таблицы были на одной странице

или на небольшом количестве страниц.

d. Убедитесь, что строки, найденные с помощью операций Index

Seek, используют как можно больше столбцов поиска.

e. Выполните денормализацию до одной таблицы, если необходимо

использовать несколько таблиц с помощью инструкций

объединения.

3. Запретите блокирование таблицы подстановки.

a. Воспользуйтесь указанием NOLOCK для предотвращения

блокирования или оператором SET LOCK_TIMEOUT в функции

с максимальным значением 1000 миллисекунд.

b. Таблицы должны существовать в базе данных master. (Когда

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

гарантировать восстановление только базы данных master.)

c. Всегда указывайте полное имя таблицы со схемой. Имя базы

данных не требуется, так как это должна быть база данных

master.

d. Не используйте триггеры для таблицы.

e. При обновлении содержимого таблицы обязательной

используйте транзакцию уровня изоляции моментального

снимка, чтобы запись не блокировала чтение. Обратите

внимание, что может также помочь использование

указания NOLOCK.

f. По возможности отключите функцию-классификатор при

изменении содержимого таблицы.

Задание:

Проработать запросы, создать функцию-классификатор базы

данных.

ЛАБОРАТОРНАЯ РАБОТА 8. СОЗДАНИЕ СЛОЖНОЙ БАЗЫ

ДАННЫХ

Типы OLAP. Преимущества и недостатки

Выбор способа хранения данных зависит от объема и структуры

детальных данных, требований к скорости выполнения запросов и частоты

обновления OLAP-кубов. В настоящее время применяются три

способа хранения данных:

MOLAP (Multidimensional OLAP)

Детальные и агрегированные данные хранятся в многомерной базе

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

манипулировать данными как многомерным массивом, благодаря чему

скорость вычисления агрегатных значений одинакова для любого из

измерений. Однако в этом случае многомерная база данных

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

детальные реляционные данные.

Преимущества MOLAP.

Высокая производительность. Поиск и выборка

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

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

Структура и интерфейсы наилучшим образом соответствуют структуре

аналитических запросов.

Многомерные СУБД легко справляются с задачами включения в

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

Недостатки MOLAP.

MOLAP могут работать только со своими собственными

многомерными БД и основываются на патентованных технологиях для

многомерных СУБД, поэтому являются наиболее дорогими. Эти

системы обеспечивают полный цикл OLAP-обработки и либо

включают в себя, помимо серверного компонента,

собственный интегрированный клиентский интерфейс, либо

используют для связи с пользователем внешние программы работы с

электронными таблицами.

По сравнению с реляционными, очень неэффективно

используют внешнюю память, обладают худшими по сравнению с

реляционными БД механизмами транзакций.

Отсутствуют единые стандарты на интерфейс, языки описания и

манипулирования данными.

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

механизма загрузки.

ROLAP (Relational OLAP)

ROLAP-системы позволяют представлять данные, хранимые в

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

локальных таблицах на файл-сервере, обеспечивая преобразование

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

Агрегаты хранятся в той же БД в специально созданных служебных

таблицах. В этом случае гиперкуб эмулируется СУБД на логическом уровне.

Преимущества ROLAP.

Реляционные СУБД имеют реальный опыт работы с очень

большими БД и развитые средства администрирования. При

использовании ROLAP размер хранилища не является таким

критичным параметром, как в случае MOLAP.

При оперативной аналитической обработке содержимого хранилища

данных инструменты ROLAP позволяют производить

анализ непосредственно над хранилищем (потому что в подавляющем

большинстве случаев корпоративные хранилища данных реализуются

средствами реляционных СУБД).

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

структуру измерений приходится вносить достаточно часто,

ROLAP системы

с динамическим представлением размерности являются оптимальным

решением, так как в них такие модификации не требуют физической

реорганизации БД, как в случае MOLAP.

Системы ROLAP могут функционировать на гораздо менее мощных

клиентских станциях, чем системы MOLAP, поскольку основная

вычислительная нагрузка в них ложится на сервер, где выполняются

сложные аналитические SQL-запросы, формируемые системой.

Реляционные СУБД обеспечивают значительно более высокий

уровень защиты данных и хорошие возможности разграничения прав

доступа.

Недостатки ROLAP.

Ограниченные возможности с точки зрения расчета значений

функционального типа.

Меньшая производительность, чем у MOLAP. Для обеспечения

сравнимой с MOLAP производительности реляционные системы

требуют тщательной проработки схемы БД и специальной настройки

индексов. Но в результате этих операций производительность хорошо

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

сравнима с производительностью систем на основе многомерных БД.

HOLAP (Hybrid OLAP)

Детальные данные остаются в той же реляционной базе данных, где

они изначально находились, а агрегатные данные хранятся в многомерной

базе данных.

Моделирование многомерных кубов на реляционной модели

данных

Схема звезда. Преимущества и недостатки

Схема типа звезды (Star Schema) - схема реляционной базы данных,

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

данных.

*Особенности ROLAP-схемы типа "звезда"*

1. Одна таблица фактов (fact table), которая сильно денормализована.

Является центральной в схеме, может состоять из миллионов строк и

содержит суммируемые или фактические данные, с помощью которых

можно ответить на различные вопросы.

2. Несколько денормализованных таблиц измерений (dimensional table).

Имеют меньшее количество строк, чем таблицы фактов, и содержат

описательную информацию. Эти таблицы позволяют пользователю

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

3. Таблица фактов и

таблицы размерности связаны идентифицирующими связями, при этом

первичные ключи таблицы размерности мигрируют в таблицу фактов в

качестве внешних ключей. Первичный ключ таблицы факта целиком

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

4. Агрегированные данные хранятся совместно с исходными.

Преимущества

Благодаря денормализации таблиц измерений упрощается

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

уменьшается количество операций соединения таблиц при обработке

запросов. Некоторые промышленные СУБД и инструменты

класса OLAP / Reporting умеют использовать преимущества схемы "звезда"

для сокращения времени выполнения запросов.

Недостатки

Денормализация таблиц измерений вносит избыточность данных,

возрастает требуемый для их хранения объем памяти. Если агрегаты

хранятся совместно с исходными данными, то в измерениях необходимо

использовать дополнительный параметр - уровень иерархии.

Схема снежинка. Преимущества и недостатки

Схема типа снежинки (Snowflake Schema) - схема реляционной базы

данных, служащая для поддержки многомерного представления

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

(Star Schema).

*Особенности ROLAP-схемы типа "снежинка"*

1. Одна таблица фактов (fact table), которая сильно денормализована.

Является центральной в схеме, может состоять из миллионов строк и

содержать суммируемые или фактические данные, с помощью которых

можно ответить на различные вопросы.

2. Несколько таблиц измерений (dimensional table), которые

нормализованы в отличие от схемы "звезда". Имеют меньшее

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

информацию. Эти таблицы позволяют пользователю быстро

переходить от таблицы фактов к дополнительной информации.

Первичные ключи в них состоят из единственного атрибута

(соответствуют единственному элементу измерения).

3. Таблица фактов и

таблицы размерности связаны идентифицирующими связями, при этом

первичные ключи таблицы размерности мигрируют в таблицу фактов в

качестве внешних ключей. Первичный ключ таблицы факта целиком

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

4. В схеме "снежинка" агрегированные данные могут храниться отдельно

от исходных.

Преимущества

Нормализация таблиц измерений в отличие от схемы "звезда"

позволяет минимизировать избыточность данных и более эффективно

выполнять запросы, связанные со структурой значений измерений.

Недостатки

За нормализацию таблиц измерений иногда

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

Задание:

В соответствии с заданием самостоятельной контрольной работы

разработать модель сущность-связь, опираясь на нотации, приведенные в

лабораторной работе.

ЛАБОРАТОРНАЯ РАБОТА 9. ПРОГРАММИРОВАНИЕ КУРСОРОВ

CURSOR – это набор строк, являющийся результатом выполнения

запроса. В один момент времени доступна лишь одна строка (текущая), по

курсору можно передвигаться и получать доступ к элементарным данным.

При объявлении курсора создается временная копия данных, которая

сохраняется в БД tempdb.

Динамический курсор – данные в курсоре могут быть изменены.

Статический курсор – данные в курсоре не меняются.

Стандартный способ объявления курсора, синтаксис в

обозначениях MS SQL Server:

DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR

FOR select_statement

[ FOR { READ ONLY | UPDATE [ OF column_name [ ,...n ] ] } ]

Примеры объявления курсоров

1. Объявляем курсор с названием MyCursor1, который содержит всю

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

вниз до последней. Курсор является динамическим.

DECLARE MyCursor1 CURSOR FOR (SELECT * FROM Authors)

2. Объявляем курсор с названием MyCursor1, который содержит всю

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

вниз до последней. Курсор является статическим.

DECLARE MyCursor1 INSENSITIVE CURSOR FOR (SELECT * FROM Authors)

3. Объявляем курсор с названием MyCursor1, который содержит всю

информацию об авторах, двигаться по нему можно в любом направлении.

Курсор является динамическим.

DECLARE MyCursor1 SCROLL CURSOR FOR (SELECT * FROM Authors)

4. Объявляем курсор с названием MyCursor1, который содержит всю

информацию об авторах, двигаться по нему можно в любом направлении.

Курсор является статическим.

DECLARE MyCursor1 INSENSITIVE SCROLL CURSOR FOR (SELECT * FROM Authors)

5. Объявляем курсор с названием MyCursor1, который содержит всю

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

вниз до последней. Курсор является динамическим. Данные доступны только

для чтения.

DECLARE MyCursor1 CURSOR FOR (SELECT * FROM Authors) FOR READ ONLY

6. Объявляем курсор с названием MyCursor1, который содержит всю

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

вниз до последней. Курсор является динамическим. Данные курсора можно

менять.

DECLARE MyCursor1 CURSOR FOR (SELECT * FROM Authors) FOR UPDATE

Операторы для работы с курсором

Прежде чем обратиться к данным курсора, его нужно после объявления

открыть.

Синтаксис оператора OPEN в обозначениях MS SQL Server:

OPEN { { [ GLOBAL ] cursor_name} | cursor_variable_name }

Пример:

DECLARE MyCursor1 CURSOR FOR (SELECT * FROM Authors)

OPEN MyCursor1

После прекращения работы с курсором, его нужно закрыть. Курсор

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

или триггера, в котором он создан.

Синтаксис оператора CLOSE в обозначенияхMS SQL Server:

CLOSE { { [ GLOBAL ] cursor_name} | cursor_variable_name}

Пример:

DECLARE MyCursor1 CURSOR FOR (SELECT * FROM Authors)

OPEN MyCursor1

--здесь операторы работы с курсором

CLOSE MyCursor1

Если курсором больше не будут пользоваться, то его необходимо

уничтожить и освободить переменную.

Синтаксис оператора DEALLOCATE в обозначениях MS SQL Server:

DEALLOCATE { { [ GLOBAL ] cursor_name} | @cursor_variable_name }

Пример:

DECLARE MyCursor1 CURSOR FOR (SELECT * FROM Authors)

OPEN MyCursor1

--здесь операторы работы с курсором

CLOSE MyCursor1

DEALLOCATE MyCursor1

FETCH – оператор движения по записям курсора и извлечения данных

текущей записи в указанные переменные. Синтаксис оператора FETCH в

обозначениях MS SQL Server:

FETCH

[ [ NEXT | PRIOR | FIRST | LAST

| ABSOLUTE { n | @nvar }

| RELATIVE { n | @nvar }

]

FROM

]

{ { [ GLOBAL ] cursor_name} | @cursor_variable_name }

[ INTO @variable_name [1 ,..., n ] ]

Пример:

DECLARE MyCursor1 SCROLL CURSOR FOR (SELECT * FROM Authors)

DECLARE @i bigint, @s char(20), @d smalldatetime

OPEN MyCursor1

FETCH FIRST FROM MyCursor1 INTO @i, @s, @d

PRINT @i

PRINT @s

PRINT @d

CLOSE MyCursor1

DEALLOCATE MyCursor1

@@FETCH_STATUS – данная функция определяет признак конца или

начала текущего курсора. Функция принимает одно из следующих значений:

0 – находимся в пределах курсора, не в конце;

1 – попытка выйти за пределы первой записи вверх (в никуда);

2 – попытка выйти за пределы последней записи вниз(в никуда).

Пример:

DECLARE MyCursor1 SCROLL CURSOR FOR (SELECT * FROM Authors)

DECLARE @i bigint, @s char(20), @d smalldatetime -- объявляем

переменные

OPEN MyCursor1 -- открываем MyCursor1

FETCH FIRST FROM MyCursor1 INTO @i, @s, @d -- устанавливаем

курсор на первую строку таблицы Authors

WHILE @@FETCH_STATUS = 0 -- пока находимся в

пределах таблицы

BEGIN

FETCH NEXT FROM MyCursor1 INTO @i, @s, @d -- записываем данные

из текущей строки в переменные

PRINT @i -- выводим code_author

в переменную i

PRINT @s -- выводим name_author

в переменную s

PRINT @d -- выводим birthday в

переменную d

END

CLOSE MyCursor1 -- закрываем курсор

DEALLOCATE MyCursor1 -- освобождаем память

Пример создания и запуска процедуры с курсором

-- пример создания процедуры

USE DB_book;

GO

IF OBJECT_ID ('Book','P') IS NOT NULL -- если уже существует

объект (процедура) с именем 'Book',

DROP PROCEDURE Book; -- удаляем этот объект

GO

CREATE PROCEDURE Book -- создаем процедуру Book,

возвращающую значение переменной cur1

@cur1 CURSOR VARYING OUTPUT -- параметры курсора в

хранимой процедуре обязательно должны быть

-- CURSOR VARYING OUTPUT

AS -- далее - тело процедуры

SET @cur1 = CURSOR FOR -- объявляем переменную

типа курсор

SELECT name_author FROM Authors; -- выводим в курсор

значения записи name_author

OPEN @cur1; -- открываем переменную

GO

--пример запуска процедуры с курсором

USE DB_book;

GO

DECLARE @MyCursor CURSOR; -- объявляем курсор

DECLARE @a CHAR(30); -- объявляем переменную @a

EXEC book @cur1 =@MyCursor OUTPUT; -- запускаем процедуру и

выводим возвращаемые значения

-- в объявленный курсор

FETCH NEXT FROM @MyCursor INTO @a; -- устанавливаем курсор на

первую запись таблицы и выводим

-- значение в переменную @a

WHILE (@@FETCH_STATUS =0) -- пробегаем по всем

строкам таблицы

BEGIN

PRINT @a; -- выводим на экран

значение переменной @a

FETCH NEXT FROM @MyCursor INTO @a; -- устанавливаем курсор на

следующую запись таблицы и выводим

-- значение в переменную @a

END;

CLOSE @MyCursor; -- закрываем курсор

DEALLOCATE @MyCursor; -- освобождаем память

GO

Задание:

В соответствии с вариантом реализовать 1 процедуру курсора.

ЛАБОРАТОРНАЯ РАБОТА 10. ДОПОЛНИТЕЛЬНЫЕ

ВОЗМОЖНОСТИ ПО ОБРАБОТКЕ ДАННЫХ

1. Специальные знаки и простейшие операторы в Transact SQL

Знак Назначение Знак Назначение

* Знак умножения » » В них заключают строковые значения, если SET QUOTED_IDENTIFIER OFF

- Знак вычитания ‘ ’ В них заключают строковые значения*

% Остаток от деления двух чисел <> Не равно

+ Знак сложения или конкатенации

(объединение двух строк в одну) [ ]

Аналог кавычек, в них можнозаключать названия идентификаторов, если в их названиях встречаются пробелы

= Знак равенства или сравнения !< Не менее чем

⇐ Меньше или равно !> Не более чем

>= Больше или равно > Больше

!= Не равно < Меньше

@ Ставится перед именем переменной . Разделяет родительские и подчиненные объекты

@@ Указывает на системные функции / Знак деления

Однострочный комментарий или

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

/* */

Многострочный комментарий

2. Идентификаторы

Идентификаторы – это имена объектов, на которые можно ссылаться в

программе, написанной на языке Transact SQL. Первый символ может

состоять из букв английского алфавита или «_», »@», »#». В качестве

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

использованы цифры и символ «$». Имя идентификатора не должно

совпадать с зарезервированным словом (среда SQL Server Management Studio

подсвечивает зарезервированные слова синим цветом).

Иногда в качестве идентификатора удобно использовать словосочетание.

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

использовать ограничители идентификаторов - квадратные скобки или

одинарные кавычки. Здесь надо обратить внимание на параметр

QUOTED_IDENTIFIER.

Для ограничителей идентификаторов при установленном параметре

SET QUOTED_IDENTIFIER ON

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

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

(режим по умолчанию).

Если использовать установленный параметр в режиме

SET QUOTED_IDENTIFIER OFF

то в качестве ограничителей идентификаторов можно использовать только

квадратные скобки, а строковые значения указываются в одинарных или

двойных кавычках.

Переменные используются для сохранения промежуточных данных в

хранимых процедурах и функциях. Все переменные считаются локальными.

Имя переменной должно начинаться с @.

3. Объявление переменных

Синтаксис в обозначениях MS SQL Server:

DECLARE @имя_переменной1 тип_переменной, …, @имя_переменнойN тип_переменной

Если тип переменной предполагает указание размера, то используется

следующий синтаксис для объявления переменных:

DECLARE @имя_переменной1 тип_переменной(размер),

…,

@имя_переменнойN тип_переменной(размер)

Пример:

DECLARE @a INT, @b numeric(10,2)

DECLARE @str CHAR(20)

4. Присвоение значений переменным и вывод значений на экран

Присвоение с помощью SET – обычное присвоение, синтаксис:

SET @имя_переменной = значение

Пример:

DECLARE @a INT, @b numeric(10,2)

SET @a = 20

SET @b = (@a+@a)/15

SELECT @b -- вывод на экран результата

Присвоение с помощью SELECT – помещение результата запроса в

переменную. Если в результате выполнения запроса не будет возвращено ни

одной строки, то значение переменной не меняется, т.е. остается старым.

Пример:

DECLARE @a INT

SELECT @a = COUNT(*) FROM Authors

Пример:

DECLARE @str CHAR(30)

SELECT @str = name_author FROM Authors

В данном примере в переменную поместится последнее значение из

результата запроса.

Сочетание ключевых слов SET и SELECT

Пример:

DECLARE @a INT

SET @a = (SELECT COUNT(*) FROM Authors)

5. Работа с датой и временем

Оператор SET DATEFORMAT dmy | ymd | mdy задает порядок следования

компонентов даты.

Пример:

SET DATEFORMAT dmy -- задает порядок следования компонентов

даты.

DECLARE @d DateTime -- объявляем переменную типа DateTime

SET @d = '30.10.1821 0:00:00' -- устанавливаем значение переменной d

SET @d = @d+1 -- прибавляем один день

SELECT @d -- выводим значение переменной d в таблице

6. Создание временной таблицы через переменную типа TABLE

Объявляется через DECLARE с указанием в скобках столбцов таблицы, их

типов, размеров, значений по умолчанию, а также индексов типа PRIMARY

KEY или UNIQUE.

Пример:

/*Объявляем таблицу*/

DECLARE @mytable TABLE ( -- Объявление переменной типа

TABLE

id INT, -- Тип целых значений

myname CHAR(20) DEFAULT 'Введите имя' -- Тип значений и в ячейке

выводится сообщение

)

INSERT INTO @mytable(id) VALUES (1) -- Добавляем строку

SELECT * FROM @mytable -- Выводим значение

Пример:

/*Объявляем таблицу*/

DECLARE @mytable TABLE(

id INT,

myname CHAR(20) DEFAULT 'Введите имя'

)

INSERT @mytable

SELECT

Code_publish,

City

FROM Publishing_house

SELECT * FROM @mytable

7. Преобразование типов переменных

Функция CAST возвращает значение, преобразованное к указанному типу:

CAST(@переменная или значение AS требуемый_тип_данных)

Пример:

DECLARE @d DateTime, @str char(20) -- объявляем переменные типа DateTime и

char

SET @d = '31.01.2005 13:23:15' -- устанавливаем значение переменной d

SET @str = CAST(@d AS Char(20)) -- устанавливаем значение str, приводим к

формату char

SELECT @str -- выводит значение переменной

Функция CONVERT возвращает значение, преобразованное к указанному

типу по заданному формату. Изучить дополнительно, по желанию.

8. Операторские скобки

BEGIN

/* В них нельзя помещать команды, изменяющие структуры объектов БД.

Операторские скобки должны содержать хотя бы один оператор.

Требуются для конструкций поливариантных ветвлений, условных и

циклических конструкций

*/

END

9. Условная конструкция IF

Синтаксис:

IF условие

Набор операторов1

ELSE

Набор операторов2

Пример:

DECLARE @a INT -- объявляет переменную

типа INT

DECLARE @str CHAR(40) -- объявляет переменную

типа CHAR

SET @a = (SELECT COUNT(name_author) FROM Authors) -- устанавливает

значение a, равное количеству

-- записей в столбце

name_author

IF @a > 10

BEGIN

SET @str = 'Количество авторов больше 10' -- если а>10, то

выводится сообщение "Количество авторов больше 10"

SELECT @str -- вывод значения

переменной str

END

ELSE

BEGIN

SET @str = ' Количество авторов = ' + str(@a) -- иначе количество

авторов равно значению переменной а

SELECT @str -- вывод значения

переменной str

END

10. Цикл WHILE

Синтаксис:

WHILE Условие

Набор операторов1

BREAK

Набор опреторов2

CONTINUE

Конструкции BREAK и CONTINUE являются необязательными. Цикл

можно принудительно остановить, если в его теле выполнить команду

BREAK. Если же нужно начать цикл заново, не дожидаясь выполнения всех

команд в теле, необходимо выполнить команду CONTINUE.

Пример:

DECLARE @a INT -- объявляет переменную типа INT

SET @a = 1 -- устанавливает значение а =1

WHILE @a < 100 -- цикл работает пока значение переменной а<100

BEGIN

PRINT @a -- вывод на экран значения переменной

IF (@a>40) AND (@a<50) -- если значение переменной а>40 и a <50

BREAK -- выход и выполнение 1-й команды за циклом

ELSE -- иначе

SET @a = @a+rand()*10 -- устанавливаем значение а равное переменной а

+ случайное число из диапазона (0, 1),

-- умноженное на 10

CONTINUE -- запускаем цикл заново, не дожидаясь

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

END

PRINT @a -- вывод на экран значения переменной

Задание:

Проработать примеры из лабораторной работы.

СПИСОК РЕКОМЕНДУЕМОЙ ЛИТЕРАТУРЫ

1. Астахова И.Ф., Мельников В.М., Толстобров А.П., Фертиков В.В. -

СУБД: язык SQL в примерах и задачах. - "Физматлит", 2009. – 168 с.

2. Харрингтон Д. - Проектирование объектно-ориентированных баз

данных. - "ДМК Пресс", 2007. – 272 с.

3. Фиайли К. – SQL. - "ДМК Пресс", 2008. – 451 с.

4. Атре, Ш. Структурный подход к организации баз данных / Ш. Атре;

пер. с англ. А. А. Александрова, В. И. Будзко; под ред. В. И. Будзко. -

М.: Финансы и статистика, 1983. - 317 с. - Библиогр.: с. 252-253. -

Прил.: с. 254-310. - Предм. указ: с. 311-313

5. Дейт, К.Дж. Введение в системы баз данных: [учеб. пособие]: [пер. с

англ.] / К. Дейт; [пер. с англ.]. - 6-е изд. - СПб.: Вильямс, 1999. - 848 с. -

(Системное программирование). - Прил.: с. 721-818. - Предм.указ.: с.

821-845. - ISBN 5-8459-0019-0

6. Базы данных. Интеллектуальная обработка информации / В. В.

Корнеев, А. Ф. Гареев, С. В. Васютин, В. В. Райх. - 2-е изд. - М. :

Нолидж, 2001. - 496с. - ISBN 5-89251-100-6

7. Ульман, Дж. Основы систем баз данных / Дж. Ульман ; пер. с англ. М.

Р. Когаловского, В. В. Когутовского ; под ред. М. Р. Когаловского. -

М.: Финансы и статистика, 1983. - 334 c. - Библиогр.: с. 318-326. - Им.

указ.: с. 327-328. - Предм. указ.: с. 329-33