Язык SQL
8.2 Стандарты и структуры языка, хранимые объекты базы
Аббревиатура SQL означает Structured Query Language, то есть "структурированный язык запросов". Рекомендуемое чтение названия [эс-кью-эл]. Встречается прочтение [сиквел]. Дело в том, что одним из предшественников SQL был язык SEQUEL [сиквел], и поэтому есть своего рода профессиональный признак —те, кто занимается давно и в хорошем коллективе, часто произносят сиквел. Это нечто похожее на то, как Пафнутия Львовича, по-моему в Москве называли Чебышев, а в Петербурге Чебыпгов и по этому произношению сразу видно — мы московские или ближе к питерским.
Язык SQL реляционно полон. Он основан на реляционном исчислении на кортежах, однако, содержит операции реляционной алгебры над множествами. Чаще используется операция
- UNION — объединение.
Иногда реализуются
- INTERSECT — пересечение;
- MINUS — разность.
8.2.1 Стандарты SQL
Стандарт языка SQL1, принятый ANSI в 1986 г., описывал только запросы. С ним вы можете поработать в WinRDBI. В настоящее время SQL1 не используется.
Промышленные СУБД основаны на следующих версиях:
- SQL2 (SQL-92) принят в 1992 г. ;
- SQL3 (SQL-99) от 1999 года;
- SQL-2003.
Набор последних расширений языка представляют как SQL-2006 и SQL-2008.
Мы будем ориентироваться на версию SQL2, но рассмотрим несколько расширений языка вне этой версии. Очевидно, изучение формальных основ языка важно, но недостаточно получить в результате только ремесленную основу — "делай раз, делай два". Важно понять, почему язык так устроен. Понимая внутреннюю структуру объекта или суть явления, вы всегда будете готовы воспринять их изменения.
Выделяются три уровня SQL—прямой, встроенный и динамический. Первый уровень обеспечивает непосредственное взаимодействие пользователя с СУБД. Встроенный SQL определяет его конструкции, вкладываемые в другие языки. В COS текст встроенного SQL помещается внутрь скобок &sql(текст_SQL). Подробности в разделе 8.9. Наконец, динамический SQL позволяет образовывать конструкции прямого SQL "на ходу" и исполнять их.
Давайте вспомним, какой путь мы с вами прошли. Во-первых, мы уже знаем, что на основе реляционной алгебры строится язык запросов, а другие языки запросов могут быть реляционно полны только в том случае, если они позволяют реализовать эквиваленты тех запросов, которые создавались на основе реляционной алгебры.
Мы уже говорили о том, что одно из главных отличий между языками, основанными на исчислениях, и языками, использующими реляционную алгебру, заключается в степени процедурности. Язык реляционной алгебры полностью процедурный, то есть в нём прописывается, как и что именно делается для получения ответа буквально по шагам. Языки, основанные на исчислениях, слабо процедурные. Написанный в этих языках запрос определяет свойства, которыми должны обладать данные, полученные в результате выполнения запроса, а вот как выполнить запрос — не указано.
Проблема в том, что в разных реализациях СУБД пути достижения результата могут быть совершенно различными. Поэтому, когда вы написали запрос, который дает вам возможность получить нужные данные, вы еще должны подумать над тем, а хорошо ли он исполняется в выбранной СУБД. На общепринятом языке говорят, что вы (или СУБД) должны выбрать оптимальный план исполнения. Ну и, естественно, создание запросов с оптимальными планами исполнения — это еще один слой программистских знаний, более глубокий, нежели просто умение написать запрос.
И еще, мы уже знаем, что на самом деле математические модели дают возможность строить только языки запросов. Потому-то в предыдущих разделах мы не занимались созданием и изменением схемы базы. Считалось, что набор отношений или набор деревьев уже существует, и мы не пытались даже заполнять его исходными данными. Вы, конечно, понимаете, что если мы хотим работать с базой данных, то должны иметь возможность задать схему приложения, изменять ее и заполнять данными. Меняется бизнес, меняются информационные потребности, и, естественно, мы должны каким-то образом всё это отслеживать. Поэтому языки, используемые в реализованных СУБД, кроме возможности писать запросы, позволяют еще создавать, изменять, удалять объекты базы и манипулировать данными. Последнее означает возможность вставлять записи, обновлять их и удалять. Вот такая сложная получается картина.
Мы с вами будем изучать SQL не вполне стандартным способом. Как всегда, у нас почти всё можно проверить на практике. Кроме того, будет исподволь готовиться материал, который позволит нам глубже, чем обычно принято в учебниках для начинающих, изучить семантику, выделить и добавить смыслы, связанные с данными.
8.2.2 Подъязыки SQL
В SQL определены следующие подъязыки:
- Язык определения данных (ЯОД). Он же Data Definition Language (DDL). Определяет структуру базы, задает хранимые объекты и привилегии доступа к ним.
- Язык манипулирования данными (ЯМД). Он же Data Manipulation Language (DML). Вставляет, обновляет и удаляет данные и выполняет запросы к ним.
- Язык управления данными/транзакциями (ЯУД). Data Control Language (DCL). Управляет транзакциями.
Одно замечание по поводу ЯОД. Мы уже обращали внимание на необходимость работы с пользователями, когда говорили о важности учёта того, кто задаёт вопросы о содержимом базы и при каких условиях это возможно. Так вот, права пользователя СУБД определяются привилегиями. Например, привилегия CREATE SESSION позволяет пользователю подключаться к базе, привилегия ALTER TABLE даёт возможность изменять таблицы и т. д.
Вообще, с пользователями обращаются по-разному в различных СУБД. Например, Oracle требует, чтобы все права созданного пользователя были прописаны полностью. То есть, если я создал пользователя, у которого есть имя и пароль, то он даже открыть сессию, то есть подключиться к СУБД, не может. Голенький, как Буратино перед тем как у него появился колпачок с кисточкой. Все привилегии нужно предоставить пользователю явно. Конечно, можно выделить собрания привилегий — роли — и использовать их, можно определить пользователя public, права которого есть у всех пользователей и т. д.
Языки манипулирования данными, позволяют вставлять, обновлять и удалять данные. Обычно язык запросов считают частью языка манипулирования данными, хотя иногда его и выделяют. Дело в том, что язык запросов строится по сути дела на основе одного шаблона SELECT, но он очень сложен.
О языке управления транзакциями мы с вами уже говорили в разделе 6.2. Помните — начало транзакции BEGIN TRANSACTION, завершение транзакции —это COMMIT или ROLLBACK. В действительности есть ещё другие инструкции, но они менее распространены.
8.2.3 Используемая терминология
И несколько замечаний о терминологии. Первое, собственно, связано не с SQL, а с тем, что в реализациях используется табличная терминология, то есть говорят не отношение, а "таблица", не "кортеж", а "строка". Атрибут называется, в зависимости от того, что вам нравится, либо столбец, либо колонка (таблица 8.1).
Термин РМД | Термин SQL |
---|---|
отношение кортеж атрибут |
таблица строка столбец, колонка |
Следует помнить, что современные версии SQL работают в расширенных реляционных моделях данных. И эти расширения настолько велики, что есть смысл говорить не о реляционных базах, а о базах данных реляционного или табличного типа. Английский термин "statement", определяющий конструкции языка, в русскоязычной литературе переводят как "оператор", "команда", "выражение". Мы будем использовать термин "инструкция", так как "команда" имеет больше процедурного смысла, чем хотелось бы, а термины "оператор" и "выражение" имеют двойной смысл.
Составные части инструкций будем называть фразами.
8.2.4 Хранимые объекты базы
Основу базы реляционного типа образуют хранимые объекты. Это таблицы, представления, индексы, триггеры, последовательности и пользователи. Пройдёмся бегло по этим понятиям. Если вам не всё будет понятно, не смущайтесь — попозже мы их рассмотрим подробнее.
В таблицах хранятся данные. Обратим внимание на вроде бы тривиальное обстоятельство: таблица не сохраняет истории изменения своих данных. Не все таблицы устроены одинаково. То, что просто называется таблица, может оказаться таблицей, организованной как куча (heap). Могут использоваться индексно организованные таблицы (IOT — index organized table). В них данные таблицы хранятся в листовых узлах дерева индекса.
Возможны совершенно оригинальные конструкции, называемые флэш-бэк-таблицами. Это на самом деле сложные структуры, которые ведут себя внешне как обычные таблицы, но отличаются тем, что позволяют просмотреть содержимое таблицы по состоянию, не только на текущий момент, но и на любой момент времени в прошлом.
Второй хранимый объект — представление (view), на программистском жаргоне — "вьюшка". Но в отличие от таблицы, содержащей данные, в базе хранится только запрос, на котором это представление построено. Представление, как и таблица, имеет имя, и когда мы обращаемся к вьюшке, то образующий её запрос комбинируется с запросом пользователя. Мы потом рассмотрим, как это делается.
Индексы. Это такая организация доступа к данным, которая может ускорить доступ, но всегда замедляет манипулирование данными. Чаще всего используют В*-индексы и побитовые индексы. Подробнее мы рассмотрим индексы в "Хранение данных и доступ к ним" .
Триггеры — это специальные процедуры, которые срабатывают при наступлении некоторого события, называемого триггерным.
База называется активной, если она делает что-то сверх того, что ее попросили. Представим, как работает ограничение целостности "первичный ключ". Как только вы пытаетесь занести данные в таблицу, ограничение целостности вызывает срабатывание своей внутренней процедуры, которая должна проверить, не повторяется ли значение первичного ключа. Если да, то ввод не допускается, а если нет — разрешается. Важно понимать, что активное поведение базы в приложениях чаще всего организуется за счёт триггеров.
Последовательности (sequence). Это ещё один новый объект, наверное, непривычный для вас. По сути дела это генератор последовательных значений со всякими возможными вариантами (последовательности циклическая, нециклическая и т.д.)
Пользователи (user). С ними связан целый ряд проблем доступа, ограничения доступа. Мы уже говорили о том, что в разных базах пользователи организованы по-разному.
На самом деле, не удаётся решить все практические задачи, используя только язык SQL. Поэтому современные СУБД имеют мощную процедурную часть, которая в разных СУБД существенно различается.
В процедурной части добавляются следующие хранимые объекты:
- Процедуры (procedure);
- Функции (function);
- Триггеры(trigger);
Существуют ещё один процедурный объект, не сохраняемый в базе. Это курсор (cursor). Вы, возможно, привыкли к тому, что курсор — это такое изображение, которое вы возите по экрану с помощью мыши или тачпада. На самом деле, в стандарте на SQL курсор определён как область памяти, которая предназначена для хранения, во-первых, названия курсора, во-вторых, запроса, на котором основан курсор, и, в третьих, данных, которые курсор выбирает из базы. Имеется указатель на строки результирующих данных.
Заметим, что повторное упоминание триггеров в процедурной части — это не ошибка. Триггеры — это процедуры специального вида.
Процедуры и функции могут группироваться в пакеты.
Замечание. Индексы будут изучаться в "Хранение данных и доступ к ним" "Хранение данных и доступ к ним". Процедурная часть СУБД в настоящем курсе рассматривается только при изучении объектных моделей.
8.2.5 Типы данных в SQL
В стандарте SQL-92 задан набор типов данных, определяемых ключевыми словами: CHARACTER, CHARACTER VARYING, BIT, BIT VARYING, NUMERIC, DECIMAL, INTEGER, SMALLINT, FLOAT, REAL, DOUBLE PRECISION, DATE, TIME, TIMESTAMP и INTERVAL. В последующих стандартах SQL этот перечень существенно расширился.
Мы будем работать с небольшой частью типов, которые реализуются во всех используемых в книге СУБД. Это типы символьных строк (character strings), точные числовые типы (exact numeric), приближённые числовые типы (approximate numeric), типы даты и времени (datetime). Типы коллекций, типы, определяемые пользователем и ссылочные типы будут рассматриваться в "Объектные модели данных" при изучении объектных моделей.
Некоторые другие типы в книге вообще не рассматриваются. Ищите их в документации СУБД, которыми будете пользоваться.
8.2.6 Типы символьных строк
- CHAR задаёт символьные строки фиксированной длины Если в спецификации указано CHAR(5), а введено значение "abc", то, например, в Oracle храниться будет константа "abc ", содержащая два пробела в конце строки.
- VARCHAR определяет символьные строки переменной длины, хранящие ровно столько символов, сколько введено, но в количестве не более указанного в спецификации VARCHAR(n). Кстати, в Oracle желательно обозначать этот тип как VARCHAR2.
Определение максимальной длины строк стандарты возлагают на реализацию. В Cache максимальная длина обоих типов 32000 символов. В Oracle максимальная длина для CHAR 2000 символов, для VARCHAR — 4000 байт.
Обратите внимание на то, что в Cache типы CHAR и VARCHAR не различимы по поведению, так как пробелы завершающие строку всегда удаляются.
8.2.7 Числовые типы
Точные числовые типы (типы с фиксированной точкой) и приближённые (с плавающей точкой) сведены в таблицу 8.2:
Стандарт SQL | Cache SQL | Oracle SQL |
---|---|---|
SMALLINT | Диапазон от -32767 до +32767 | NUMBER(5) |
INTEGER | Диапазон от -2147483647 до +2147483647 | NUMBER(n), где |
NUMBER | NUMBER(n,s) | NUMBER(n,s) |
У Cache в типе NUMBER(n,s) точность n равна 21, но может быть изменена. Тип NUMBER без аргументов определяет целые числа в диапазоне от -9223372036854775807 до +9223372036854775808. В Oracle n<38, а значение s, определяющее положение младшего разряда от —84 до +127.
8.2.8 Типы даты и времени
Используемые в книге типы даты и времени сведены в таблицу 8.3.
Стандарт SQL | Cache SQL | Oracle SQL |
---|---|---|
DATE |
DATE Внутренний формат — число дней от 31.12.1840 |
DATE От 01.01.4712 до РХ до 31.12.9999 после РХ |
TIME | TIME | TIMESTAMP |
TIMESTAMP | TIMESTAMP |
TIMESTAMP Дробная часть секунды от 0 до 9 разрядов (по умолчанию 6) |
Значения типа DATE состоят из трёх компонентов: года, месяца и даты. Значение года определяется летоисчислением от Рождества Христова. Один из возможных выходных форматов "yyyy-mm-dd", где все составляющие — год, месяц и день — представляются десятичными числами. В Cache начальная дата —31.12.1840.
Значения типа TIME составляются из значений часа, минуты, секунды и, возможно, дробных долей секунды.
В типе TIMESTAMP объединяются данные предыдущих двух типов.