Языки запросов

Но обеспечение целостности данных – это далеко не все, что требуется от СУБД. Требование поддержания согласованности данных в нескольких файлах не позволяет при построении информационной системы обойтись простой библиотекой функций: такая система должна обладать некоторыми собственными данными (их принято называть метаданными), определяющими целостность данных. Именно наличие метаданных позволяет СУБД описывать и использовать произвольные данные сначала в рамках навигационной, а затем - реляционной модели.

В нашем примере информационная система должна отдельно сохранять метаданные о структуре файлов СЛУЖАЩИЕ и ОТДЕЛЫ, а также правила, определяющие условия целостности данных в этих файлах (принято считать, что правила также составляют часть метаданных).

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

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

1. Установить номер отдела, в котором работает указанный служащий.

– Для этого в файле СЛУЖАЩИЕ необходимо отыскать запись, для которой значение поля СЛЖ_ИМЯ = 'ПЕТР ИВАНОВИЧ ФЕДОРОВ'.

– Для найденной записи необходимо определить значение поля СЛЖ_ОТД_НОМЕР. Пусть в файле СЛУЖАЩИЕ это будет запись, для которой значение поля СЛЖ_ОТД_НОМЕР = n.

2. Выбрать из файла ОТДЕЛЫ запись, для которой значение поля ОТД_НОМЕР = n.

3. Определить значение поля ОТД_ЧИСЛ в таблице ОТДЕЛЫ для записи, у которой значение поля ОТД_НОМЕР = n.

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

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

Например, при наличии языка запросов SQL наш запрос можно было бы выразить в следующей форме (запрос1):

SELECT ОТД_ЧИСЛ

FROM СЛУЖАЩИЕ, ОТДЕЛЫ

WHERE СЛЖ_ИМЯ = 'ПЕТР ИВАНОВИЧ ФЕДОРОВ' AND

СЛЖ_ОТД_НОМЕР = ОТД_НОМЕР;

Это пример запроса с «полусоединением», который характеризуется тем, что:

– запрос адресуется к двум файлам – СЛУЖАЩИЕ и ОТДЕЛЫ,

– но данные выбираются только из файла ОТДЕЛЫ.

Условие СЛЖ_ОТД_НОМЕР = ОТД_НОМЕР всего лишь «ограничивает» интересующий нас набор записей об отделах до одной записи, если Петр Иванович Федоров действительно работает на данном предприятии. Если же Петр Иванович Федоров не работает на предприятии, то условие СЛЖ_ИМЯ = 'ПЕТР ИВАНОВИЧ ФЕДОРОВ' не будет удовлетворяться ни для одной записи файла СЛУЖАЩИЕ, и поэтому запрос выдаст пустой результат.

Возможна и другая формулировка того же запроса (запрос2):

SELECT ОТД_РАЗМЕР

FROM ОТДЕЛЫ

WHERE ОТД_НОМЕР =

(SELECT СЛЖ_ОТД_НОМЕР

FROM СЛУЖАЩИЕ

WHERE СЛЖ_ИМЯ = 'ПЕТР ИВАНОВИЧ ФЕДОРОВ');

Это пример запроса на языке SQL с вложенным подзапросом. Во вложенном подзапросе выбирается значение поля СЛЖ_ОТД_НОМЕР из записи файла СЛУЖАЩИЕ, в которой значение поля СЛЖ_ИМЯ равняется строковой константе 'ПЕТР ИВАНОВИЧ ФЕДОРОВ'. Если такая запись существует, то она единственная, поскольку поле СЛЖ_ИМЯ является уникальным ключом файла СЛУЖАЩИЕ. Тогда результатом выполнения подзапроса будет единственное значение – номер отдела, в котором работает Петр Иванович Федоров. Во внешнем запросе это значение будет ключом доступа к файлу ОТДЕЛЫ, и снова будет выбрана только одна запись, поскольку поле ОТД_НОМЕР является уникальным ключом файла ОТДЕЛЫ. Если же на данном предприятии Петр Иванович Федоров не работает, то подзапрос выдаст пустой результат, и внешний запрос тоже выдаст пустой результат.

Приведенные примеры показывают, что при формулировке запроса с использованием SQL можно не задумываться о том, как будет выполняться этот запрос. Среди метаданных базы данных будет содержаться информация о том, что поле СЛЖ_ИМЯ является ключевым для файла СЛУЖАЩИЕ (т. е. по заданному значению имени служащего можно быстро найти соответствующую запись или убедиться в том, что запись с таким значением поля СЛЖ_ИМЯ в файле отсутствует), а поле ОТД_НОМЕР – ключевое для файла ОТДЕЛЫ (и более того, оба ключа в соответствующих файлах являются уникальными), и система сама воспользуется этим.

Наиболее вероятным способом выполнения запроса в обеих формулировках будет выборка записи из файла СЛУЖАЩИЕ со значением поля СЛЖ_ИМЯ, равным строке 'ПЕТР ИВАНОВИЧ ФЕДО­РОВ', взятие из этой записи значения поля СЛЖ_ОТД_НОМЕР и выборка из таблицы ОТДЕЛЫ записи с таким же значением поля ОТД_НОМ.

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

SELECT СЛЖ_ИМЯ, СЛЖ_НОМЕР

FROM СЛУЖАЩИЕ

WHERE СЛЖ_СТАТ = "НЕТ";

и система сама выполнит необходимый полный просмотр файла СЛУЖАЩИЕ, поскольку поле СЛЖ_СТАТ не является ключевым, и другого способа выполнения не существует.

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

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

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

– определить процедуру, которая должна выполнить данный запрос на языке SQL. В нашем случае это бет процедура, которую условно назовем SELECT;

– в качестве входного атрибута этой процедуры по ключевому слову FROM определяется имя таблицы СЛУЖАЩИЕ;

– в качестве критерия выборки по ключевому слову WHERE определяется условие СЛЖ_ СТАТ = "НЕТ";

– в качестве выходных данных процедуры после SELECT ключевого слова определяются поля СЛЖ_ИМЯ, СЛЖ_НОМЕР выбранных записей;

– после определения всех необходимых данных процедура запускается и после выполнения всех необходимых операций на физическом уровне пользователь получает результат в виде списка значений для полей СЛЖ_ИМЯ, СЛЖ_НОМЕР.

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

Сразу напрашивается решение:

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

– определить спецификации этих процедур в качестве языковых конструкций для работы со списками. Например, для вставки записей в список мы могли бы определить спецификацию вида INSERT («Иванов И.И.», «ЭВМд-31»).

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

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

Для хранения таких данных мы должны выделить некоторую таблицу или таблицы с фиксированными полями и записать в эти таблицы всю необходимую информацию о структуре списка:

– наименования полей записей;

– типы данных полей записей, что и определяет размер каждого поля;

– местоположение каждого поля в записи;

– общий размер записи, определяемый по размерам отдельных полей;

– связь полей с той или иной структурой записи;

– наименования типов структур.

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

В случае работы с табличными структурами хранение данных о произвольной базе данных в фиксированных таблицах, т.е. в строго формализованном виде, также позволяет строить унифицированные процедуры для обработки данных базы данных, содержащей произвольное число таблиц, с любым числом атрибутов (в пределах допускаемых СУБД) и строк, между которыми могут иметь место различные взаимосвязи. Эти унифицированные процедуры составляют ядро языка SQL в виде инструкций CREATE, DROP, SELECT, INS­ERT, UPDATE, DELETE и т.д., обеспечивающих унифицированную обработку произвольных данных, хранящихся в таблицах баз данных.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: