+ All Categories
Home > Documents > Kristofer Alen Oracle PL,SQL

Kristofer Alen Oracle PL,SQL

Date post: 08-Feb-2016
Category:
Upload: vlad-kopanev
View: 246 times
Download: 1 times
Share this document with a friend
Popular Tags:
369
Oracle Press oirv/ Издательство Лори
Transcript

Oracle Press

oirv/

ИздательствоЛори

f

ORACLE Oracle Press TM

Oracle PL/SQL 101

Christopher Allen

Osborne/McGraw-Hill

П : ' ..-• , . - . , -

' . " ' * , . • / ' - ; •'.- , • ' • > ' . :

101: Oracle PL/SQL

Кристофер Аллен

: .

Издательство "Лори"

Oracle PL/SQL 101Christopher AllenCopyright © 2001 by The McGraw-Hill Companies, Inc. All rights reserved.

Osborne/McGraw-Hill2600 Tenth StreetBerkeley, California 94710U.S.A.

ISBN 0-07-212606-X

:

101: Oracle PL/SQLКристофер Аллен

Переводчик Т. МоскалевНаучный редактор А. ГоловкоКорректор И. ГришинаВерстка Е. Самбу

© Издательство "Лори", 2001

Изд. № : OAI (03)ЛР №: 070612 30.09.97г.ISBN 5-85582-139-0

Подписано в печать 20.10.2001 Формат 70 х 100/16Бумага офсет № 1 Гарнитура Ньютон Печать офсетнаяПеч. л. 23 Тираж 3200 Заказ № 725Цена договорная

Издательство "Лори". 123557 Москва, Б.Тишинский пер., д.40, корп. 2Телефон для оптовых покупателей: (095) 259-01-62WWW.LORY-PRESS.RU

Отпечатано в типографии ООО "Типография ИПО профсоюзов Профиздат"109044, Москва, ул. Крутицкий вал д. 18

Посвящается Грейс

только как использовать каждую возможность, ни и дли MCI или п^лли Mv»»*».Приведенные примеры отражают типы задач, которые вам придется решатьпри использовании SQL и PL/SQL в своей работе. Я постарался сделать текстдостаточно занимательным, чтобы у вас не угас интерес к этой увлекательнойтеме. Если у вас появятся идеи относительно того, как можно улучшить книгу,пишите мне по адресу plsqll 01 ©yahoo.com. Я обещаю не забывать, что вы оказыва-ете мне любезность, присьшая не только положительные, но и отрицательныеотзывы. Вы также можете написать мне по этому адресу, чтобы получить сцена-рии, приведенные в книге.

С пожеланиями успехов в изучении SQLКристофер Аллен

БлагодарностиМне вновь довелось сотрудничать с выдающимся коллективом Oracle Press.

Мой поклон издателю Брандону Нордину, а также его помощнику, вицегпре-зиденту Скотту Роджерсу, под руководством которых книжная серия OraclePress приобрела то качество, которое она имеет сейчас. Находиться в такойкомпании — большая честь для меня.

Спасибо Монике Фолтис, Россу Долу, Клэр Сплан и Лизе МакКлейн, сде-лавшим издательский процесс настолько приятным, насколько это вообщевозможно. Отдельное спасибо Равиндре Дани за неоценимый вклад в редакти-рование. Сердечно жму руку Джереми Джудсону, которщй неизменно ортаетсяголосом здравого смысла и просто классным парнем.

Содержание'

Благодарности vi

Об авторе vii•чмЬмм)

Предисловие vii

;

Часть IОсновы баз данных——

Глава 1 Введение в базы данных 3

Что такое база данных? 4Таблицы 4Строки/записи 5Столбцы/поля 5

Чем база данных отличается от электронной таблицы? 5Хранение многих строк 7Одновременное обслуживание многих пользователей 7Безопасность 7Реляционные свойства. 8Ограничения, гарантирующие качество данных 9

Вопросы для повторения 9Практическое задание 10

Как вам пригодятся эти знания? 10При администрировании базы данных 10При разработке программ 11При проведении бизнес-анализа 11Если вы просто хотите знать, как лучше использоватьбазу данных 11

История SQL 12

Категории команд SQL 12Определение данных 13Манипулирование данными 13

Содержание

Управление денными , , i, , . > : , , ! : - , . ~j ^^:.с.£ I, •; ; 13Выборка Данньйс 14Управление транзакциями 14

Итоги 14Вопросы 16Ответы на вопросы 17

•' >, • .• • . • ' • • •"-:•' '.«•" - : ' 1 ':/'.] I . . ; '

Глава 2 Сохранение и выборка данных: основы 19

Первые шаги 20Создание таблицы 20Вставка записей 21Выбор записей 21Удаление таблицы 23

Создание таблиц 25Именование таблиц и столбцов 25

Правила р., 25Рекомендации 26

. • ; • . , ' ; , ' '- .' • : .<• ,Создание более сложной таблицы 31

Как в Oracle хранится текст • . 31Как Oracle сохраняет числа 34Как Oracle сохраняет даты 36

, - Определение структуры таблицы 39Столбцы NULL и NOT NULL 39

Вставка данных — дополнительные приемы 42Как вставлять записи с null-значениями 42

, Как вставлять данные с апострофами 45Просмотр данных — дополнительные приемы 46

Выбор определенных столбцов 46Изменение порядка столбцов 47Вычисления с использованием данных из таблицы 47

Математические операторы : ; < ,. 48Что такое выражение? 49Приоритет операторов 50

Соединение двух и более частей текста 51Присваивание столбцам псевдонимов 52

Итоги 52Вопросы 55Ответы на вопросы 57

xi

Глава 3 Более сложные манипуляции с данными 59

Ограничение диапазона выбираемых записей 60Фильтрация записей по числовым значениям 61

Выбор записей по одиночному значению 61Выбор записей по диапазону значений i r , ; ;v 53Исключение записей , . . . . ; . . - у . , .- , г ] 63Выбор записей по группе допустимых значений 64

Фильтрация записей по тексту 65Использование шаблонов ;65

Фильтрация записей по датам 67Выбор записей по null-значениям 68

Изменение порядка записей 69Сортировка по отдельным столбцам .; V? 69Сортировка по нескольким столбцам 70

Отображение только уникальных значений ./, , л.,, , 72

Выбор из DUAL , , 74

Модификация данных в таблице 75

Удаление записей из таблицы 77Удаление записей, соответствующих заданному критерию 77Удаление всех строк 77

Удаление записей без указания критериев 77Усечение таблицы 78

Управление транзакциями 79Отмена транзакций DML 79Доступность данных для других пользователей * S2Явное и неявное завершение 84

Итоги 84Т» . • • . • . , ' : • . ' : • , - . • ; . • .' • - : . '•-. •' i : ,. - ••!• .-•'*«•Вопросы 86

Ответы на вопросы 87. - . ; • ' ' • ' - ;.

Глава 4 Управление SQL'Plus 89

Редактирование предыдущих команд 90Использование текстового редактора , 90

Использование команды EDIT,-. • • : 91Построчное редактирование 92

Использование команды CHANGE S>2Выбор строки при построчном редактировании 94

Копирование и вставка 95

xii Содержание

Очистка экрана SQL*Plus 97Настройка среды SQL*Plus 97

Настройка с использованием меню SQL*Plus 98Настройка с использованием команд 100

Сохранение настроек среды 100Форматирование выходных данных SQL*Plus 101

Форматирование чисел 102Выравнивание количества десятичных знаков 102Добавление разделителя групп разрядов 102Добавление знака денежной единицы 103Другие полезные коды форматов 103

Форматирование текста 103Форматирование заголовков столбцов 105

Буферизация выходных данных на диске 107Файлы сценариев SQL 108

Создание файла сценария 108Запуск сценария 109Использование переменных в файлах сценариев 109

Переменные подстановки 110Команда ACCEPT 111

Итоги 111Вопросы 112Ответы на вопросы 113

' -

ЧастьSQL для профессионалов

.

Глава 5 Встроенные функции SQL 117

Часто используемые однострочные функции 119Системные переменные 119

SYSDATE 119USER 120USERENV 121

Числовые функции 121ROUND 122TRUNC 123

Текстовые функции 12 4

; xiii

UPPER, LOWER и INITCAP 124LENGTH 126SUBSTR 127

INSTR 129LTRIMnRTRIM 133

Функции для работы с датами 136SYSDATE и TRUNC 136ADD_MONTHS 137LAST_DAY 139

MONTHS_BETWEEN 141Функции преобразования данных 142

TO_CHAR 142TO_DATE 147

Прочие функции 151DECODE 152NVL 156Пример: Использование изученных функцийдля решения реальной задачи 156Вставка комментариев в SQL-сценарии 158

Часто используемые групповые функции 160SUM 161COUNT 162AVG 163MIN 163MAX 163

Группирование данных с помощью конструкцииGROUP BY 164Включение и исключение групп с помощьюконструкции HAVING 166

Итоги 166;~

Вопросы 167иг

Ответы на вопросы 168

' ' " ' . - •-'•'•• ••'Глава 6 Индексы и ограничения 171

Индексы 173Индексы в базах данных 174Когда индексы помогают? 176Как создаются индексы 177Различные типы индексов 178

Индексы В*-дерева 178Битовые индексы 179

xiv ,_.,.. Содержание

Обеспечение целостности данных: ограничения 181Что такое ограничение? 182Как создавать ограничения * 182

NOT NULL 182UNIQUE 183CHECK 186

Разрешение и запрещение существующих ограничений 190Изменение и удаление существующих ограничений 192Где следует определять ограничения — в базе данныхили приложении? 193

Связи между таблицами 194Введение в моделирование данных 194Использование ограничений для установления связеймежду таблицами 196

Создание первичного ключа 198Создание ограничения внешнего ключа 199

, Написание операторов SELECT, отображающих данныеиз более чем одной таблицы 202Внешние соединения 207Операторы соединения 208

UNION 209UNION ALL 210INTERSECT 211MINUS 211

Написание подзапросов - 211Что такое подзапрос? 211Типичные проблемы, решаемые с помощью подзапросов 211Однострочные подзапросы 212Многострочные подзапросы • 214Подзапросы, возвращающие более одного столбца 215

**••,'* ,-" '•' .' '"• -,'' *t ••'". "•• : • ''••' ' " . ' . " ) * ; ' ' ' -

Итоги 216.Вопросы 217Ответы на вопросы 218

'- •"' ' " - . • . " - ' . ,..; . ' " ' ' . " • • • ,

Глава 7 Другие полезные средства Oracle 221

Перенос данных между таблицами 225Перенос данных с помощью INSERT 226Создание новой таблицы на основе уже существующей 228

Переименование таблиц 229

XV

Изменение структуры таблицы , , v 229Добавление столбцов 229Изменение типа данных столбца ; , 230Изменение null-опций 231

Представления 232Создание представления 233Удаление представлений j 235Изменение определения представления 235Анализ первых JV записей 235

Другие объекты базы данных 236Последовательности 236

Создание последовательности 236Использование последовательности 237Модификация существующей последовательности 239

Синонимы 239Создание синонима 241Модификация существующего синонима 242

Словарь данных Oracle 242Опрос словаря данных для получения информациио пользователях и базе данных 242Использование различных представлений словаря данных 242

Итоги 244Вопросы 245Ответы на вопросы 246

Часть IIIСоздание программ на PL/SQL . .

Глава 8 Введение в PL/SQL 251

Что такое PL/SQL? 252Общие сведения о PL/SQL 255,SQL, PL/SQL и SQL*Plus: кто есть кто 256Хранимые процедуры, функции и триггеры 257

Хранимые процедуры , 257Хранимые функции 258Триггеры 259

Хранимые процедуры в сравнении с SQL-сценариями 259

xvi Содержание

Структура блока PL/SQL 259Секция заголовка 260Секция объявлений 261Выполняемая секция 261Секция исключений 262

Создание простой PL/SQL-процедуры 263Вызов процедур и функций 264

Переменные и константы PL/SQL 266Объявление переменных PL/SQL 266Объявление констант PL/SQL 267Присваивание значений переменным 267Использование переменных 269

Управляющие структуры в PL/SQL 269Оператор IF 270Циклы 272

LOOP 272Цикл WHILE 274Цикл FOR 275

Курсоры 275Объявление курсора и атрибуты курсора 276Записи PL/SQL 277Использование команд OPEN, FETCH и CLOSE 278Курсорный цикл FOR 278Конструкция WHERE CURRENT OF 279

Вложенные циклы и пример курсора 280Обработка ошибок 282

Исключения 282Системные исключения 284Исключения, определяемые программистом 286

Итоги 288Вопросы 289Ответы на вопросы 290

Глава 9 Другие средства PL/SQL 293

Соглашения о кодировании 298Подробнее о взаимодействии PL/SQL и сервера Oracle 299

Динамическое объявление типов переменных и записиPL/SQL 300

XVII

DML в PL/SQL, или неявные курсоры 305Сравнение явных и неявных курсоров 310

Операции с временем 311Измерение времени в программе 311Использование команды TIMING для счетареального времени 312

Пакеты PL/SQL 315

Триггеры 321Типы триггеров 323Пример триггера 324Модификация триггеров 327Тонкости, касающиеся триггеров 327

ODBC 328• • - • ' - '

Создание ODBC-соединения 328Применение ODBC-соединений 333

Просмотр и редактирование данных Access 333Импорт данных Oracle в Excel 335

Итоги 338Вопросы 338Ответы на вопросы 339

Глоссарий 341

Часть

Основы баз данных

Глава

Введение в базы данных

4 Глава!

Добро пожаловать в удивительный мир баз данных! "Что же здесь удивитель-ного?" — можете спросить вы. Ответ будет касаться не самих баз данных, а ихсодержимого: информации. Информации, которая может облегчить вамжизнь, упорядочить хаос и помочь сделать такие открытия, на которые в про-тивном случае у вас никогда бы не нашлось времени. Когда вы учитесь грамот-но использовать базы данных, вы одновременно узнаете, как управлятьполучением информации. От наличия этого фундаментального навыка зави-сит, сможете ли вы справляться с проблемами.

Что такое база данных?В самом простом виде база данных представляет собой список с информацией

(или множество связанных списков). Система управления базами данных (СУБД) —это специализированная программа-менеджер, управляющая таким списком.

Базы данных присутствуют в повседневной жизни почти каждого челове-ка. Например, телефонный справочник — это представление базы данных набумаге. Он содержит определенную информацию о людях, упорядоченнуютаким образом, чтобы вы могли быстро находить нужные сведения. Если всправочнике есть раздел со списками коммерческих организаций, часто на-зываемый "желтыми страницами", информация в этом разделе упорядоченапо виду их деятельности, а в пределах каждого вида деятельности — по назва-ниям организаций.

Адресная книга, корешки чековой книжки, телевизионная программа—этотоже примеры баз данных.

Вы наверняка использовали базы данных в Интернете. При поиске книгиили компакт-диска через Web-сайт возвращаемая информация извлекается избазы данных. (Недавно я разрабатывал именно такую базу данных для круп-нейшей звукозаписывающей компании.) Сайты для проведения аукционов вреальном времени также являются большими базами данных, содержащимиинформацию о покупателях, продавцах, товарах, предложениях и обратнойсвязи. Поисковые системы, такие, как AltaVista и Yahoo! — это гигантские базыданных, содержащие ключевую информацию о миллионах Web-страниц.

ТаблицыБазы данных всегда разрабатываются для хранения информации опреде-

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

EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY HIRE_DATE

1024 , Scott Campbell 63000 17-FEB-98

2048 Linda Hammond 68000 15-JAN-99

3072 Dave Anthony 69000 11-APR-OO

4096 Tiff Berlin 66000 24-DEC-01

Введение в базы данных 5

Самое важное, что нужно помнить о таблице, состоит в следующем: она хра-нит информацию об объектах одного типа. Таблица с информацией о людях небудет содержать информацию о газонокосилках! Точно так же, как таблица синформацией об учебных курсах не будет содержать сведения о слушателяхэтих курсов. Если в базе данных нужно хранить информацию об объектах болеечем одного типа, как это почти всегда и бывает, используются несколько таб-лиц. Например, для правильного отслеживания учебных курсов база данныхдолжна иметь (как минимум) одну таблицу для преподавателей, еще одну—длякурсов, третью — для аудиторий и четвертую — для студентов. Раздельное хра-нение информации разного типа позволяет организовать базу данных весьмаэффективным способом, а следовательно, сделать ее простой в использовании.

Строки/записиПростая таблица служащих, показанная выше, содержит информацию о че-

тырех людях. Каждому из них отведен свой горизонтальный ряд ячеек. Каждыйтакой ряд называется строкой (row), а содержащиеся в нем данные — записью(record). Одна строка содержит информацию об одном и только одном объекте,указанном в имени таблицы. Например, в таблице служащих каждая строка со-держит информацию только об одном служащем. И наоборот, информация окаждом служащем хранится только в одной строке. Таблицадроектируется так,чтобы вся информация об объекте, на который указывает ее имя, занимала толь-ко одну строку. (Очень скоро вы научитесь делать все это самостоятельно.)

Столбцы/поляКаждая строка содержит несколько единиц информации. В примере со слу-

жащими к ним относятся личный идентификатор, имя и т. д. Эти информаци-онные единицы хранятся в столбцах (columns) таблицы. Точка пересечениястроки и столбца — например, имя определенного служащего — называетсяполем (field). Поле содержит одну единицу информации о чем-либо (например,телефонный номер одного человека).

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

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

Чем база данных отличаетсяот электронной таблицы?

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

6 Глава 1

Таблица Записи

СвенМарк

ШраммКинг.

Джеймс БридструпВирджиния Хэвлин

Майкл

(555)234-5678

Любит мотоциклы, фотографию,игру на гитаре и больших собак

Поля

Рис. 1.1. Информация о друзьях, расположенная на индексных карточках

Строки

Столбцы

2 Майкл Хабнер

КомментарииЛюбит мотоциклы, фотографию,

(555)234-5678 гпгшеЬпйпфгпа!! net 'игру на гитаре и больших собак

.3 Вирд4 Джей

5 'Марк

6 Свен

н"< > и'

:иния Хэвл*1C Брид

Кинг

Шрам

^Лист1/ПиС'

. (555)3труп (555) 4i

'(555)51

и (555) 6;

г'/гьстэУ

5-6789 *avel!rtg>r6-7890 jbndslrupfc

7-8901 :mking@>m

8-9012 sschltmm

Неутомимая пурiai1.net 'просто хорошийmail. ret Прозвище: Mr £

Если бы у меняii пйЕ незамужняя свс

Перед тем. как jВосточное побе|

Smaii net убедиться, что о

' И . , •

шествекница ичеловекmooove _J|iUM

pa.v \s]гтеть на : J" :|ежье. нужноi HE работает ^И

1 ' >lDi

ТПоля

— Таблица

Рис. 1.2. Информация о друзьях, занесенная в электронную таблицу

Введение в базы данных

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

Хранение многих строкПоскольку электронные таблицы создавались главным образом для финан-

совых вычислений (функционально это аналог страниц бухгалтерской книги сформулами, куда нужно подставлять значения), они не рассчитаны на хране-ние такого количества строк, как в коммерческих базах данных. Для электрон-ных таблиц характерно ограничение в 65 536 строк. Это довольно много дляэлектронной таблицы, но не для базы данных. (Например, недавно я использо-вал один Web-сайт, чтобы узнать, сколько сообщений в Интернет-конферен-циях содержат слово "Oracle". Критерию поиска соответствовало примерно1 200 000 сообщений, и все они хранились в базе данных сайта. Эта база данныхсодержит более миллиона записей только о сообщениях, связанных с Oracle;представьте, сколько всего там записей!) Коммерческая база данных может со-держать несколько миллионов строк, а большие компании имеют базы данныхс миллиардами строк. Никакая электронная таблица не справится с такимиобъемами информации!

Одновременное обслуживаниемногих пользователей

Базы данных лежат в основе деятельности многих компаний, поэтому онидолжны обеспечивать одновременный доступ к одной и той же информациидля большого числа людей. Чтобы понять, почему это важно, представьте сетьрозничных магазинов, в которых установлена сотня компьютеризированныхкассовых аппаратов. В день активных продаж многие из этих аппаратов будутвыполнять транзакции в одно и то же время. Если вам придется простоять укассы, ожидая завершения всех остальных транзакций, вы наверняка будетеразочарованы и больше не придете в этот магазин. Точно так же поступит мно-жество других покупателей, и компания понесет убытки. Системы более круп-ного масштаба, например, для бронирования авиабилетов, могут ежесекундноиметь дело с тысячами запросов, и если каждому из них придется ожидать вы-полнения всех остальных, то такая система будет раздражающе медлительной.Способность обслуживать большое количество одновременно работающих по-льзователей является одним из ключевых свойств базы данных. Хорошо спро-ектированная база данных может одновременно отвечать на запросы тысяч идаже миллионов пользователей при сохранении удовлетворительной произво-дительности.

БезопасностьБазы данных содержат часть самой важной информации компаний например

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

8 Глава 1

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

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

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

В большой системе баз данных — скажем, с 1000 и более пользователей —управление всеми видами привилегий быстро станет нереальным, если уста-навливать их для каждого пользователя в отдельности. К счастью, базы данныхOracle позволяют объединять привилегии в так называемую роль (role). Когда вбазу данных добавляются новые пользователи, им присваиваются роли, кото-рые содержат все необходимые привилегии. Такой подход хорошо работает,поскольку круг должностных обязанностей в коммерческих организацияхобычно четко определен, а привилегии, необходимые для каждого пользовате-ля, непосредственно связаны с его должностью. Так, бухгалтерскому клеркупотребуется вводить данные из счетов, но возможно, что изменение введенныхданных будет разрешено только старшему бухгалтеру, а с данными о зарплатахне сможет работать никто, кроме главного бухгалтера. Каждая из этих трехдол-жностей является хорошим кандидатом на роль базы данных. Например, роль"бухгалтерский клерк" может быть присвоена всем рядовым служащим бухгал-терии. Роли безопасности помогают гарантировать, что каждый пользовательбудет иметь необходимые ему привилегии. Роли также позволяют с легкостьюприсваивать новую привилегию группе: достаточно добавить привилегию кроли этой группы, и дело сделано.

Реляционные свойстваПоскольку в базе данных разнотипная информация хранится в разных таб-

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

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

Введение в базы данных 9

Итак, у нас есть таблица для хранения данных о товарах. Теперь потребуетсядругая таблица, в которой будут храниться заказы на эти товары. Каждая строкатаблицы ORDER будет содержать дату, время, адрес заказчика и общую суммузаказа. В этой таблице также должно быть указано, на какой товар сделан заказ,для этого достаточно включить в каждую запись идентификатор товара. Здесь ипригодится связь: единственной информацией о товаре, содержащейся в заказе,является его идентификатор. Описание товара, цена и прочая информация втаблице ORDER отсутствует. Почему? Помимо всего прочего, это привело бы кненужной трате места, поскольку описание каждого товара, его цена и другиесведения уже содержатся в таблице PRODUCT. Чтобы эта связь заработала, не-обходимо лишь сообщить базе данных, что идентификатор товара в заказе яв-ляется тем же самым уникальным идентификатором, который присутствует втаблице PRODUCT. Зная об этом, база данных сможет соединять информациюиз обеих таблиц и представлять результат соединения в одной строке, как еслибы он брался из одной таблицы.

База данных, реализующая описанную технику связывания записей из раз-ных таблиц, называется реляционной (relational database). В коммерческих базахданных нередко можно встретить таблицы, имеющие связи с десятками другихтаблиц. Это делается по многим причинам, которые будут подробно рассмот-рены в главе 6. Противоположный подход состоит в том, что создается одна бо-льшая таблица, в которой информация повторяется каждый раз, когда этонеобходимо. Таблица такого типа называется плоским файлом (flat file). Это на-звание отражает ее двумерность — только строки и столбцы, никаких связей сдругими таблицами.

Ограничения, гарантирующие качество данныхИнформация, хранящаяся в базе данных, иногда поступает непосредствен-

но с других машин: автоматизированных датчиков, таймеров или счетчиков.Однако большая часть данных вводится людьми, а людям свойственно делатьошибки. При проектировании базы данных можно определить ограничения(constraints), т.е. условия, которым должны удовлетворять данные в некоторомполе, чтобы запись была принята базой данных. Эти ограничения могут бытьочень простыми, гарантируя, например, что цена всегда будет положительнымчислом, или более сложными: в частности, можно потребовать, чтобы иденти-фикатор, введенный в таблицу заказов, существовал в таблице PRODUCT, иличтобы некоторые поля записи вводились только при наличии определенныхзначений в других полях. За счет автоматизации этих функций контроля каче-ства база данных помогает обеспечивать "чистоту" хранимых данных.

Вопросы для повторения1. Какое свойство таблицы является самым существенным?

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

3. Какие ключевые свойства базы данных позволяют использовать ее дляхранения больших объемов информации?

10 Глава 1

Практическое задание

1. Возьмите четыре чистых листа бумаги. Они могут быть любого разме-ра — подойдут как индексные карточки, так и просто тетрадные листы.

• 2. Напишите на каждом листе имя, фамилию, номер телефона и адресэлектронной почты своего друга или родственника (вам потребуетсясделать это для четырех людей — по одному на лист).

3. Разложите листы на столе так, чтобы они не касались друг друга.

4. Сдвиньте листы вплотную, расположив их в два ряда по два листа. Те-перь каждый лист будет касаться двух соседних.

5. Склейте листы в этом положении скотчем.

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

7. Обведите прямоугольником каждый элемент, который будет соответст-вовать строке таблицы. Напишите в центре склеенного листа слово"строки" и проведите от него стрелки к каждому прямоугольнику.

8. В завершение напишите фразу "столбец e-mail" на любом свободномместе и соедините ее одной длинной линией с каждым элементом, ко-торый будет храниться в этом столбце таблицы.

Как вам пригодятся эти знания?В наши дни все очень заняты, и если вы читаете книгу по PL/SQL, то навер-

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

При администрировании базы данныхРаботать администратором базы данных Oracle (DBA — Oracle database ad-

ministrator) без знания SQL просто невозможно, а без знания PL/SQL (супер-множества SQL) — очень трудно. Дело в том, что многие из типовых задачадминистрирования выполняются с использованием SQL, и при этом доволь-но часто требуются средства PL/SQL. Хотя в Oracle есть ряд программ, позво-ляющих выполнять администрирование с помощью удобного графическогоинтерфейса пользователя (graphic user interface, GUI), эти инструменты содер-жат порядочное количество ошибок. Кроме того, некоторые задачи можно вы-полнить быстрее, работая непосредственно с SQL, а в ряде систем соединения сбазами данных устанавливаются через текстовые терминалы, на которых нель-зя запускать GUI-инструменты. Важность SQL отражает тот факт, что в про-грамме сертификации администраторов, разработанной корпорацией Oracle исостоящей из пяти отдельных экзаменов, первый экзамен целиком посвященSQL и PL/SQL.

Введение в базы данных 11

При разработке программНезависимо от того, разрабатываете ли вы программы с использованием

Java, C++ или продукта Oracle Forms Developer, велика вероятность, что раноили поздно вам потребуется писать некоторый SQL-код для прямого взаимо-действия с базой данных. Многие разработчикиспотыкаются на этом этапе, де-лая ошибки, которые оборачиваются потерей времени и производительности.Затраты времени на тщательное изучение SQL многократно себя окупят. Доку-ментация, поставляемая вместе с Oracle, содержит разделы, в которых описановзаимодействие с Oracle средствами языков Java, С, C++ и COBOL.

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

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

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

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

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

12 Глава!

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

История SQLНебольшой экскурс в историю позволит получить лучшее представление об

изучаемом предмете. История SQL развивалась параллельно с историей реля-ционных баз данных. В1969 г. д-р Эдгар Ф. Кодд опубликовал в серии исследо-вательских отчетов IBM сообщение под названием Derivability, Redundancy, andConsistency of Relations Stored in Large Data Banks. Там описывался подход кструктурированию баз данных, основанный на использовании связанных таб-лиц, который значительно отличался от принятого в то время подхода с плос-кими файлами. Это сообщение имело пометку "для ограниченногораспространения" и поэтому не получило широкой известности. Кодд перера-ботал свои концепции и в 1970 г. опубликовал их в статье под названием A Rela-tional Model of Data for Large Shared Data Banks в журнале ACM (Association ofComputer Machinery). Реляционная модель, описанная Коддом, в 1974 г. былаиспользована в прототипе реляционной системы управления базами данных(РСУБД), названной System R. Описывая системный язык запросов в ноябрь-ском номере IBM Journal ofR&D за 1976 г., корпорация IBM использовала длянего название Structured English QUEry Language (SEQUEL, язык структуриро-ванных английских запросов). В ходе эволюции языка название изменилось наStructured Query Language (SQL, произносится как сиквел (sequel) или "S-Q-L").Первая коммерческая версия SQL была выпущена в 1979 году корпорациейOracle (вто время называвшейся Relational Software Inc.).

В 1986 г. к работе подключился Американский национальный институтстандартов (ANSI), опубликовавший официальный стандарт SQL с кодовымназванием ANSI ХЗ. 135-1986. В следующем году этот стандарт был опублико-ван Международной организацией по стандартизации (ISO) как ISO 9075-1987.Спецификация SQL дважды расширялась — в 1992 и 1999 гг. Текущая специ-фикация состоит из пяти частей, имеющих названия ANSI/ISO/IEC9051-1-1999 - 9051-5-1999.

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

Короче говоря, SQL — это универсальный инструмент, необходимый каж-дому, кто регулярно работает с базами данных.

Категории команд SQLКоманды SQL делятся на функциональные группы, что облегчает их запо-

минание. Вот эти группы:

• Определение данных (Data Definition)

Введение в базы данных 13

j

• Манипулирование данными (Data Manipulation)

• Управление данными (Data Control)

• Выборка данных (Data Retrieval)

• Управление транзакциями (Transaction Control)

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

!

Определение данныхВсе основные СУБД, в том числе и Oracle, являются так называемыми плат-

формами баз данных. Это означает, что они предоставляют среду, очень хоро-шо поддерживающую работу с таблицами, но не содержат никаких заранеесозданных таблиц. Вы должны сами определять состав и конфигурацию храни-мых данных. Для этого в SQL существует ряд специальных команд: CREATE,ALTER, DROP, RENAME и TRUNCATE.

Эти команды входят в группу, называемую языком определения данных(Data Definition Language, DDL).

Манипулирование даннымиДопустим, вы научились создавать таблицы. Что делать дальше? Разумеется,

поместить в них данные. В SQL есть команда IN SERT, позволяющая добавлятьданные в таблицы. После того как данные вставлены, их можно изменять, ис-пользуя команду UPDATE, или удалять, используя команду DELETE.

Эта категория команд называется языком манипулирования данными (DataManipulation Language, DML).

Управление даннымиРанее в этой главе мы обсуждали средства безопасности. (Я уверен, что вы о

них помните, но если кто-то, читающий через ваше плечо, забыл, советую за-глянуть в раздел "Чем база данных отличается от электронной таблицы?".) Воз-можность предоставлять некоторым пользователям доступ к определеннымтаблицам, в то время как другим это запрещено, обеспечивается за счет присва-ивания пользователям привилегий на таблицы или действия. Объектная приви-легия разрешает пользователю выполнять определенные действия надтаблицей (или другими объектами базы данных„о которых пойдет речь в следу-ющих частях этой книги). Пример объектной привилегии — возможностьвставлять записи в таблицу EMPLOYEE. Системная привилегия, напротив, раз-решает пользователю выполнять действия определенного типа во всей базеданных. Примером системной привилегии будет возможность вставлять запи-си в любую таблицу базы данных.

Привилегии базы данных присваиваются и удаляются с помощьюSQL-команд GRANT и REVOKE, соответственно. Эти команды относятся ккатегории, называемой языком управления данными (Data Control Language,DCL).

14 Глава 1

Выборка данныхСмысл помещения информации в базу данных состоит в том, чтобы полу-

чать ее обратно контролируемым образом. В этой категории всего одна коман-да — SELECT, но она имеет широкий набор параметров, обеспечивающихогромную гибкость. Вероятно, именно эту команду вы будете использоватьчаще всего, особенно если планируете обращаться к SQL из другого языка про-граммирования, такого, как Java или C++.

Управление транзакциямиSQL позволяет отменять любые из последних команд DML до того, как они

будут применены к базе данных. (Кстати, какие команды называются команда-ми DML? Если вы затрудняетесь с ответом, просмотрите раздел "Категориикоманд SQL" еще раз.) После выполнения одной или нескольких команд DMLвы можете ввести либо команду COMMIT для сохранения изменений в базеданных, либо команду ROLLBACK для их отмены ("отката").

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

ИтогиВ самом простом виде база данных представляет собой список с информа-

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

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

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

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

Введение в базы данных 15

называются записью (record). Таблица проектируется так, чтобы информация окаждом объекте занимала только одну строку.

Каждая строка содержит несколько элементов информации. Эти элементыхранятся в столбцах (columns) таблицы. Точка пересечения строки и столбца —например, имя определенного служащего — называется полем (field). Поле со-держит один элемент информации о чем-либо — например, телефонный номеродного человека.

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

Умение работать с SQL может принести пользу в самых разных областях. Безэтого не обойтись, если вы планируете стать администратором баз данных, по-скольку многие задачи администрирования выполняются с использованиемкоманд SQL. При разработке программ на Java, С, C++ и COBOL вам с боль-шой вероятностью потребуется использовать команды SQL для вставки, об-новления и удаления данных. При проведении бизнес-анализа знание SQLпозволит вам взаимодействовать с базой данных напрямую, извлекая инфор-мацию нужным вам способом, и не ограничиваться предопределенными за-просами, созданными кем-то другим. Аесли вы просто хотите знать, как лучшеработать с базой данных, понимание SQL поможет разобраться в том, как поль-зоваться разнообразными продуктами и сервисами в повседневной жизни.

В основу SQLпoлoжeны концепции, выдвинутые д-ром Эдгаром Ф. Коддоми впервые опубликованные в 1969 году. SQL фактически стал стандартнымязыком для взаимодействия со всеми основными СУБД. Его команды делятсяна следующие функциональные категории: определение данных, манипулиро-вание данными, управление данными, поиск данных и управление транзакци-ями. Команды языка определения данных (DDL) используются дляопределения способа хранения данных; к ним относятся CREATE, ALTER,DROP, RENAME и TRUNCATE. Команды языка манипулирования данными(DML) позволяют работать с данными; к ним относятся INSERT, UPDATE иDELETE. Команды языка управления данными (DCL) позволяют определить,какие операции с базой данных смогут выполнять те или иные пользователи.Это делается с помощью объектных привилегий, управляющих доступом к от-дельным объектам базы данных, а также системных привилегий. Последниепредставляют собой глобальные привилегии, действующие во всей базе дан-ных. К командам DCL относятся GRANT и REVOKE. Единственной командойпоиска данных является SELECT, но за счет своих многочисленных вариацийона наверняка станет самой популярной в вашем арсенале. Для управлениятранзакциями SQL предоставляет команду COMMIT, сохраняющую послед-ние изменения в базе данных; команду ROLLBACK, отменяющую последниеизменения, и команду SAVEPOINT, позволяющую отменять действие тольконекоторых команд DML.

2 Зак. 725

16 Глава 1

Вопросы1. Что из перечисленного ниже относится к примерам баз данных?

A. Сообщения на первых полосах газет

B. Телефонные справочники

C. Корешки чековых книжек

О. Web-сайты для проведения аукционов в реальном времени, заказатоваров и поиска

E. Объявления о продаже

F. Киноафиши

2. Какое свойство таблицы является наиболее существенным?

А. Имеет строки и столбцы

В'. Может быть связана с другими таблицами

C. Хранит информацию об объектах одного типа

D. Содержит записи и поля

3. Установите соответствие между терминами из левого столбца и их опи-саниями из правого столбца.

Термин Описание

Строка Хранит информацию об объектах одного типа(например, о людях или товарах)

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

Столбец Один ряд ячеек таблицы

Поле Совокупность однотипных данных, хранимыхв таблице (например, все телефонные номера или всефамилии)

, Таблица Данные, содержащиеся в строке таблицы

База данных Содержит одну единицу информации о чем-либо

Ограничение Набор связанных таблиц

4. Что из перечисленного ниже входит в число причин, по которым базаданных является наилучшим выбором для работы с большими объема-ми корпоративных данных?

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

B. Функционирование только на PC

C. Возможность одновременно обслуживать тысячи пользоМателей

D. Обеспечение безопасности на уровне объектов

Введение в базы данных 17

/E. Возможность связывания многих таблиц

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

5. Кто был пионером в разработке теории реляционных баз данных?

A. Э. Ф. Скиннер

B. Эдгар Уинтер

C. Э. Ф. Кодд

D. Эдгар Пис

6. Установите соответствие между категориями команд SQL из левогостолбца и командами из правого столбца.

Категория команд SQL Команды

Язык определения данных (DDL) GRANT и REVOKE

Язык манипулирования данными (DML) SELECT

* Язык управления данными (DCL) CREATE, ALTER,DROP, RENAMEи TRUNCATE

Поиск данных COMMIT, ROLLBACKnSAVEPOINT

Управление транзакциями INSERT, UPDATEи DELETE

Ответы на вопросы1. В, С, D, F Телефонный справочник; корешки чековых книжек;

Web-сайты для проведения аукционов в реальном времени, заказа това-ров и поиска; киноафиши.

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

2. С Хранит информацию об объектах одного типа.

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

3. Термин Описание

Строка Один ряд ячеек таблицы

18 Глава 1

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

Столбец Совокупность однотипных данных, хранимыхв таблице (например, все телефонные номераили все фамилии)

Поле Содержит один элемент информациио чем-либо

Таблица Хранит информацию об объектах одного типа(например, о людях или товарах)

База данных Набор связанных таблиц

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

4. А, С, D, Е, F Возможность хранения миллиардов строк; возможностьодновременно обслуживать тысячи пользователей; обеспечение безо-пасности на уровне объектов; возможность связывания многих таблиц;возможность определять ограничения, которым должны удовлетворятьданные, вводимые в базу данных.

Объяснение Наличие любой из возможностей, за исключением В,служит веским основанием для использования базы данных в качествехранилища больших объемов информации. Большие базы данных могутфункционировать не только на PC — например, СУБД Oracle доступнадля компьютеров с операционными системами Unix, Linux, WindowsNT и рядом других. Доступность только на PC является недостатком, ане достоинством, поскольку другие операционные системы разработа-ны для промышленного использования и в целом стабильней систем,предназначенных для персональных компьютеров.

5. С Э. Ф. Кодд

Объяснение Д-р Эдгар Ф. Кодд опубликовал основополагающиеработы по теории реляционных баз данных в 1969 г. Его считают "от-цом" реляционных баз данных.

6. Категория команд SQL Команды

Язык определения данных (DDL) CREATE, ALTER,DROP, RENAMEи TRUNCATE

Язык манипулирования данными (DML) INSERT, UPDATEи DELETE

Язык управления данными (DCL) GRANT и REVOKE

Поиск данных SELECT

Управление транзакциями COMMIT, ROLLBACKи SAVEPOINT

. . .'•••

...... ---

или

Глава

Сохранениеи выборка данных:

основы

20 ;•'.._ • Глава2

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

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

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

Чтобы выполнить описанные ниже шаги, вам потребуется запуститьSQL*Plus — программу, поставляемую Oracle, которая позволяет соединяться с , *базой данных. Для этого администратор базы данных должен сообщить вамидентификатор пользователя, пароль и имя базы данных, а также показать, какзапускается SQL*Plus в вашей системе. (Описание настройки базы данныхOracle и конфигурирования SQL* Plus выходит за рамки данной книги. Если выхотите узнать об этом подробнее, обратитесь к книге Стива Бобровски Oracle Sifor Windows NT Starter Kit (Oracle Press, 2000) или к документации Oracle.)

Предполагая, что вы имеете всю необходимую информацию, a SQL*Plusсейчас показывает приглашение SQL>, приступим к упражнению.

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

же, как и электронная таблица: она состоит из столбцов, и вы можете помещатьв нее строки данных. Перед тем как добавлять строки, необходимо определитьстолбцы, которые будут составлять таблицу. Это делается с помощью командыCREATE TABLE. Чтобы увидеть, как работает эта команда, введите в строкеприглашения SQL> следующий код:

CREATE TABLE plsqll01_test_l '(

first_name CHAR(15),

last_name CHAR (20)

Набрав эту команду, нажмите клавишу ENTER. Ваш экран должен выгля-деть так, как показано на рис. 2.1.

Таким способом SQL* Plus сообщает вам, что команда была успешно выпол-нена. (Если вы увидели другое сообщение, проверьте, правильно ли введенакоманда, и повторите ее еще раз.) Теперь у вас есть таблица в Oracle! Далее мыподробно рассмотрим структуру команды, использованной для создания таб-лицы. А сейчас перейдем непосредственно к работе с таблицей и введем.в неенесколько записей.

Сохранение и выборка данных: основы

Be Ed» Se«ch Options Ц*':-

SQL> CREATE TflBLE plsqUDI test_1 (2 first_nane CHflR(15),3 last_nane CHAR(2в)* )s ;

Table created.. f

SQL>

^y

' ' ; ' . , '

Рис. 2.1. Результаты команды CREATE TABLE 1

Вставка записейДля помещения записей в таблицу используется команда INSERT. Введите

следующую строку после приглашения SQL>:

INSERT INTO plsqll01_test_l VALUES ('Jane', 'Smith'•);''

Набрав эту команду, нажмите клавишу ENTER. Вы должны увидеть сооб-щение, показанное на рис. 2.2.

Теперь таблица содержит первую запись. Чтобы добавить вторую запись,наберите после приглашения SQL> следующую строку и нажмите ENTER:

INSERT INTO plsq!101_test_l VALUES ('Cristopher', 'Alien*);'

Теперь в таблице содержатся две отдельные записи. Как вы могли заметить,команда INSERT помещает записи в таблицу по одной. Но как их увидеть? Чи-тайте дальше.

ПримечаниеНачиная с этого момента, в тексте не будет упоминаться о том, чтопосле ввода команды нужно нажать клавишу ENTER. Вы просто по-лучите указание ввести одну или несколько строк SQL-команд. Что-бы SOL*Plus воспринимал команды, нажимайте ENTER послекаждой строки.

Выбор записейЧтобы увидеть вставленные в таблицу записи, введите следующую команду:

SELECT * FROM plsql!01_test_l;

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

22 Глава 2

A. Oiacle SQL'Plus

File ЕЛ Search Options HelpSQL> CREATE TABLE plsq!101 test_1 (

2 first_name CHAR(15),3 last_name CHAR(20)t >5 ;

Table created.i • - . ' • . ' '.

SQL>SQL> INSERT INTO plsql1B1_test_1 UALUES ('Jane

1, 'Smith');

1 row created.

SQL>

тшс

Рис. 2.2. Результаты команды INSERT

* Oiacle SQL'Plu МШКЗFile Edit Search f l p t i o n s Help . . . , , .

SQL> CREATE TABLE plsql1B1_test 1 (2 first_naroe CHAR(15),3 last_nane CHAR(20)4 )5 ;

Table created.

SQL>SQL> INSERT INTO plsql101_test_1 UflLUES ('Jane', 'Snith');

1 row created.

SQL> INSERT INTO plsq!1B1_test_1 UALUES ('Christopher', 'Allen');

1 row created.

SQL>

SQL> SELECT « FROM plsqll01_test_1;

FIRST NAME LAST NAME

Jane

Christopher

SQL> |

Smith

Allen

Рис. 2.3. Результаты команды SELECT

Сохранение и выборка данных: основы 23

Удаление таблицыЧтобы завершить первое погружение в мир баз данных, удалим созданную

таблицу.

ПримечаниеУдаление таблицы — это серьезный шаг! Он необратим!Используйте эту команду только при абсолютной уверенностив том, что записи таблицы больше не нужны.

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

DROP TABLE plsql!01_test_l;

На экране должен появиться ответ, показанный на рис. 2.4.

*. Oracle SQL-Plus

File Edit Search Options HelpSQL> CREflTE TABLE plsqllB1_test_1 (

first_nane CHAR(15),

HI3E3

3it5

last name CHfiR(2B)

Table created.

SQL>SQL> INSERT INTO plsqllB1_test_1 UALUES ('Jane

1, 'Smith

1);

1 row created.,.,!••. .-, , - .

SQL> INSERT INTO plsqll81_test_1 UALUES ('Christopher1, 'Allen

1);

1 row created.

SQL>SQL> SELECT * FROM plsqll01_test_1;

FIRST NOME LAST NAME

JaneChristopher

Smithflllen

SQL> DROP TABLE plsqll01_test_1;

Table dropped.

S«JL>

Рис. 2.4. Результаты команды DROP TABLE

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

SELECT * FROM plsqllOl test_l;

24 Глава 2

Вьг должны увидеть сообщение, аналогичное показанному на рис. 2.5. Припомощи этой команды вы пытались выбрать записи из несуществующей табли-цы. В ответ Oracle вывел четыре строки текста. Первая дублирует ту частькоманды, где была обнаружена ошибка. (Поскольку ваша команда состояла то-лько из одной строки, она и показана.) Вторая строка содержит звездочку (*)под тем местом команды, где начали появляться проблемы. В третьей строкеобъявляется о возникновении ошибки, а четвертая сообщает, в чем состоит этаошибка — в данном случае причина в том, что указанная вами таблица(PLSQL101_TEST_1) не существует.

* IJmr.le SfJI.'HIus ИГ»1 Е

£1в ££ £urch Qptions ДОрSQL> СВЕЙТЕ TABLE plsqlt01 test_1 (

2 first папе CHflR(i5),3 list MM CHAR(20)»•;•: лу..5 ;

Table created.

SQL>SQL> INSERT INTO plsql101_test_1 UflLUES ('Jane', 'Snith');

1 row created.

SQL> INSERT INTO plsq!101_test_1 UHLUES ('Christopher', 'Allen');

1 row created.

SQL>SQL> SELECT » FROM plsql181_test_1;

FIRST NAME LAST НИНЕ

Jane SnithChristopher Allen

SQL> DROP TABLE plsq!101_test_1;

Table dropped.

SQL> SELECT * FROM plsqliei test 1;SELECT • FROM plsq!101_test~1

«ERROR at line 1:ORA-60942: table or view does not exist

SO.LH

Рис. 2.5. Попытка выбрать записи из несуществующей таблицы

Сохранение и выборка данных: основы • 25

Только что выполненные операции продемонстрировали основные .функ-ции таблицы: прием, хранение и выдачу информации. Отлично! Можете отло-жить книгу в сторону.

Конечно, я шучу. Эти шаги демонстрируют лишь крошечную часть того, чтоможно делать с базой данных. Чтобы узнать больше, продолжайте чтение.

Создание таблицБазы данных предназначены для хранения информации, а она содержится в

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

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

• Ограничивают длину вводимых данных

• Запрещают ввод записей, в которых не заполнены определенныестолбцы

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

• Имеют логическую связь с другими таблицами

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

Именование таблиц и столбцовПрисваивая имена таблицам и столбцам, вы должны следовать определен-

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

ПравилаПеречисленные ниже правила обязательны для любой таблицы или столб-

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

• Максимальная длина имени таблицы или столбца равна 30 символам.

• Имена таблиц и столбцов могут содержать буквы, цифры и символподчеркивания (_). (Есть еще пара специальных символов, которыеможно использовать в случае острой необходимости, но в обычнойработе это не принесет ничего, кроме проблем, поэтому лучшеограничиться буквами, цифрами и символом подчеркивания.): с

26 Глава 2

• Имена таблиц и столбцов должны начинаться с буквенного символа.Имя может содержать цифры или символы подчеркивания, но в любомслучае должно начинаться с буквы.

• Символы верхнего и нижнего регистров в именах таблиц и столбцовсчитаются одинаковыми.

• Имя таблицы или столбца не должно содержать пробелы.

• В Oracle таблицы присваиваются пользователям; по умолчанию ониприсваиваются тому пользователю, который их создал . Каждая изтаблиц должна иметь имя, отличное от имен других таблиц этогопользователя. Иными словами, у пользователя не может быть двухтаблиц с одним и тем же именем. (Однако разные пользователи могутбез проблем создавать таблицы с одинаковыми именами.) Все столбцыв пределах таблицы должны иметь уникальные имена.

• Некоторые слова представляют собой команды и параметры Oracle, aследовательно, не могут использоваться в качестве имен таблиц илистолбцов. Вероятно, вы не сможете запомнить все эти слова, но на нихстоит хотя бы взглянуть. Слова, имеющие специальное значение,приведены в таблице 2.1.

;~jp СоветЕдинственный способ гарантировать, что имя таблицы никогдане совпадет с зарезервированным словом Oracle, — это пред-варять его аббревиатурой, обозначающей систему, к которойотносится таблица. Например, в системе Accounts Payable("счета кредиторов") имя каждой таблицы может начинаться с

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

• Имена таблиц следует записывать в единственном, а не множественномчисле. Всем и так понятно, что таблица PRODUCT будет содержатьзаписи о многих товарах, поэтому нет необходимости отмечать это вимени таблицы. К тому же, глядя на диаграммы с таблицами базыданных (они рассматриваются в главе 7), вы сможете переходить оттаблицы к таблице, говоря себе примерно следующее: "PURCHASEORDER ссылается на PRODUCT...".

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

Сохранение и выборка данных: основы 27

Таблица 2.1. Команды и зарезервированные слова Oracle,которые не могут быть именами таблиц и столбцов

ACCESS

АИ

ANY

AT

BACKUP

BITMAP

CACHEINSTANCES

CHANGE

CHOOSE

CLOSED,

ACCOUNT

ALL_ROWS

ARCHIVE

AUDIT

BECOME

BLOB

CANCEL

CHAR

CHUNK

CLUSTER

ACTIVATE

ALLOCATE

ARCHIVELOG

AUTHENTICATED

BEFORE

BLOCK

CASCADE

CHAR_CS

CLEAR

COALESCE

ADD

ALTER

ARRAY

AUTHORIZATION

BEGIN

BODY

CAST

CHARACTER

CLOB'

COLUMN

ADMIN

ANALYZE

AS

AUTOEXTEND

BETWEEN

BY

CFILE

CHECKPOINT

CLONE

COLUMNS

AFTER

AND

ASC

AUTOMATIC

BFILE

CACHE

CHAINED

CHECK,

CLOSE'••

COMMENTCACHEDOPEN_CURSORS

COMMIT COMMITTED COMPATIBILITY COMPILE

COMPRESS

CONTENTS

CPU_PER_CALL

CURSOR

DATAOBJNO

DECIMAL

DELETE

DISMOUNT

DUMP

ENTRY

COMPUTE

CONTINUE

CPU_PERSESSION

CYCLE

DATE

DECLARE

DEREF

DISTINCT

EACH

ESCAPE

CONNECT

CONTROLFILE

CREATE

DANGLING

DBA

DEFAULT

DESC

DISTRIBUTED

ELSE

ESTIMATE

CONNECT.TIME

CONVERT

CURRENT

DATABASE

DEALLOCATE

DEFERRABLE

DIRECTORY

DML

ENABLE

EVENTS

COMPOSITELIMIT

CONSTRAINTS

COST

CURRENTSCHEMA

DATAFILE

DEBUG

DEFERRED

DISCONNECT

DOUBLE

END

EXCEPTIONS

COMPLETE

CONSTRAINT

COUNT

CURRENT,USER

DATAFILES

DEC

DEGREE

DISABLE

DROP•

ENFORCE

EXCHANGE

28 Глава 2

Таблица 2.1 (продолжение)

EXCLUDING

EXPLAIN

FAST

FOR

FULL -.n.

GROUP

HEAP

INCLUDING

INDICATOR

INSTANCES

INTO

KILL

LIMIT

LOG ov

EXCLUSIVE

EXTENT

FILE

FORCE

FUNCTION

GROUPS

IDENTIFIED

INCREMENT

INITIAL

INSTEAD

IS

LAYER

LINK

LOGFILE

EXECUTE

EXTENTS

FIRST.ROWS

FOREIGN

GLOBAL

HASH

IDLEJIME

IND_ PARTITION

INITIALLY

INT

ISOLATION

LESS

UST

LOGGING

EXEMPT

EXTERNALLY

FLAGGER

FREEUST

GLOBAL.NAME

HASHKEYS

IF

INDEX

INITRANS

INTEGER

ISOLATIONLEVEL

LEVEL

LOB

LOGICALREADS_PER_

EXISTS

FAILEDLOGINATTEMPTS

FLOAT

FREEUSTS

GLOBALLY

HAVING

IMMEDIATE

INDEXED

INSERT

INTERMEDIATE

KEEP

LIBRARY

LOCAL

LOGICALREADS PER

EXPIRE

FALSE

FLUSH

FROM

GRANT

HEADER

IN

INDEXES

INSTANCE

INTERSECT

KEY

LIKE

LOCK

LONG

-\v.?- i -

MANAGE MASTER MAX

CALL

MAXARCHLOGS MAXDATARLES MAXEXTENTS

MAXIN-STANCES

MAXVAUJE

MINVALUE

MULTISET,

NESTED

MAXLOGRLES

MEMBER

MODE

NATIONAL

NETWORK

MAXLOGHISTORY

MIN

MODIFY

NCHAR

NEW

MAXLOG-MEMBERS

MINEXTENTS

MOUNT

NCHAR.CS

NEXT

MAXSIZE

MINIMUM

MOVE

NCLOB

NLS.CHA-алггсоссг

MAXTRANS

MINUS

MTSDISPATCHERS

NEEDED

NLS_

Сохранение и выборка данных: основы 29

Таблица 2.1 (продолжение)

NLS ISOCURRENCY

NOAUDIT

NONE

NORESET-LOGS

NOTHING

OBJECT

OID

OPCODE

ORDER

PARTITION

PCTFREE

PERCENT

PRECISION

PRIVILEGE

QUEUE

REAL

REFRESH

RESIZE

REUSE

ROW

NLSLANGUAGE

NOCACHE

NOMAXVALUE

NOREVERSE

NOWAIT

OBJNO

OIDINDEX

OPEN

OVERFLOW

PASSWORD• . ;

PASSWORDVERIFYFUNCTION

PCTVERSION

PRESERVE

PRIVILEGES

QUOTA

REBUILD

REFERENCES

RESETLOGS

REVERSE

ROWID

NLS NUMERICCHARACTERS

NOCOMPRESS

NOMINVALUE

NORMAL

NULL

OBJNO_REUSE

OLD

OPTIMAL

ORGANIZATION

PASSWORDUFEJIME

PASSWORDGRACEJIME

PERMANENT

PRIMARY

PROCEDURE

RANGE

RECOVER

REFERENCING

RESOURCE

REVOKE

ROWLABa

NLS.SORT

NOCYCLE

NOORDER

NOS SPECIALCHARS

NUMBER

OF

ON

OPTIMIZERGOAL

OWN

PASSWORDLQCKJIME

PCTINCREASE

PLAN

PRIOR

PROFILE

RAW

RECOVERABLE

RENAME

RESTRICTED

ROLE

ROWNUM

NLSTERRITORY

NOFORCE

NOOVERIDE

NOSORT

NUMERIC

OFF

ONLINE

OPTION

' PACKAGE

PASSWORDREUSEJWX

PCTTHRES-HOLD

PLSQLDEBUG

PRIVATE

PUBLIC

RBA

RECOVERY

REPLACE

RETURN

ROLES

ROWS

•' '• : 404 '" "'

NOARCHIVELOG

NOLOGGING

NOPARALLEL

NOT

NVARCHAR2

OFFLINE8 « ;

ONLY

OR

PARALLEL.. :,-...' :,••;•..

PASSWORDREUSEJIME

PCTUSED'-r'

^POSTTRANSACTION

PRIVATESGA

PURGE ~

READ

REF

RESET

RETURNING

ROLLBACK

RULE

30 Глава 2

Таблица 2.1 (.продолжение)

SAMPLEif.-.

SD.ALL

SELECT

SET

SKIMUNUSABLE.INDEXES

SPLIT

STOP

••SUM

SYSOPER

TABNO

TIME

TRANSAC-TION

TX

UNDO

UNTIL

USE

VALUES

WHEN

WRITE

SAVEPOINT

SDJNHIBIT

SEQUENCE

SHARE

SMALLINT

SQLJRACE

STATEMENTID

SUCCESSFUL

SYSTEM

TEMPORARY

TIMESTAMP

TRANSITIONAL

TYPE

UNION

UNUSABLE

USER

VARCHAR

WHENEVER

XID

SCANINSTANCES

SD_SHOW

SERIALIZABLE

SHARED

SNAPSHOT

SQLCODE

STATISTICS

SWITCH

TABLE

THAN

TO

TRIGGER

UBA

UNIQUE

UNUSED

USING

VARCHAR2 •

WHERE

SCHEMA

SEG_BLOCK

SESSION

SHARED_POOL

SOME

.

SQLERROR

STORAGE

SYNONYM

TABLES

THE

TOPLEVEL

TRIGGERS

i

UID

UNLIMITED

UPDATABLE

VALIDATE

VARRAY

WITH

SCN

SEG_FILE

SESSIONCACHEDCURSORS

SHRINK

SORT

STANDBY

STRUCTURE

SYSDATE

TABLESPACE

THEN

TRACE

TRUE

UNARCHIVED

UNLOCK

UPDATE

VALIDATION

VARYING

WITHOUT

SCOPE

SEGMENT

SESSIONSPER USER

' 5

SIZEISPECIFI-CATION

START

STORE

SYSDBA

TABLESPACENO' "THREAD

TRACING

TRUNCATE

iUNDER

UNRECOVER-ABLE

USAGE

VALUE

VIEW

WORK

Сохранение и выборка данных: основы 31

/ . •

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

и длину. Таблицы Oracle могут хранить любые разновидности данных — текст,числа, даты, изображения, звуковые файлы и т. д. Каждый тип данных имеетопределенный набор свойств. Самые распространенные типы данных табли-цы — это текст, числа и даты. Далее на конкретных примерах будет показано,как указать каждый из этих типов. Одновременно с этим выясним, чем одинтип данных отличается от другого.

Как в Oracle хранится текстДля начала нужно выяснить, что в базе данных считается текстом. Это не

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

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

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

(800)555-1212

Цифры и символы, из которых он состоит, можно интерпретировать мате-матически, но это не будет иметь никакого смысла. То же самое справедливодля почтовых индексов (zip-кодов) вида 12345-6789 и номеров социальногообеспечения (Social Security Numbers, SSN) вида 123-45-6789. В каждом из этихслучаев данные состоят из цифр и математических символов, но они никогдане будут складываться, вычитаться и т.д. Данные такого типа лучше всего хра-нить в текстовом столбце.

И! СоветЛ Как определить, какой столбец использовать для хранения чисел —

текстовый или числовой? Подумайте, будете ли вы когда-нибудьвыполнять над этими числами математические операции (напри-мер, сложение или усреднение). Если да, то используйте числовойстолбец. В противном случае более подходящим может оказатьсятекстовый столбец.

Oracle предлагает несколько разных способов хранения текста, каждый изкоторых рассчитан на определенное применение. Самый простой текстовыйтип данных — тот, который использовался при создании таблицы в предыду-щем упражнении; он называется CHAR (сокращение от "character"). Определяястолбец CHAR, вы одновременно указываете максимальное количество сим-

32 Глава 2

волов, которое он может содержать. Для этого используется команда следую-щего вида (не вводите ее — это лишь пример):

CREATE TABLE имя_таблицы (имя_столбца CHAR(л));

Этот простой пример показывает, как можно создать таблицу с однимстолбцом типа CHAR. Обратите внимание, что имя таблицы, имя столбца и ко-личество символов набраны курсивом.' Курсив, используемый в примерахкоманд, указывает на то, что в этом месте вы должны подставить свои собствен-ные данные, а не вводить сам курсивный текст. В данном случае курсив пока-зывает, в каком месте команды нужно указать имя таблицы, имя столбца идлину столбца. Такие примеры, демонстрирующие, как должна быть составле-на команда, показывают синтаксис команды.

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

СоветПри определении столбца типа CHAR можно не указывать его дли-ну. В этом случае будет использована длина по умолчанию, рав-ная 1. Однако определение столбца без явного указания длинысчитается проявлением небрежности. Указывайте длину столбцаво всех случаях, даже если она равна 1.

Другим типом данных для хранения текста является VARCHAR2 (сокраще-ние от "variable-length character"). Этот тип, как и тип CHAR, позволяет хранитьтекст, числа и специальные символы. Чем же они отличаются? Если столбецCHAR предназначен для хранения, скажем, десяти символов текста, он хранитровно десять символов, даже если введенные данные имеют меньшую длину.Оставшееся место заполняется пробелами. Так, имя "George", введенное встолбец CHAR(IO), будет в действительности сохранено как "George" с четырь-мя пробелами на конце. Поскольку нет никакого смысла хранить дополнитель-ные пробелы в столбцах с содержимым переменной длины, столбцы CHARлучше всего подходят для текстовых данных, длина которых фиксирована, на-пример, для обозначений штатов, стран или пола.

В отличие от этого столбец типа VARCHARxpamiT ровно столько символов,сколько было введено. Столбец VARCHAR( 10), в который введено имя "George",будет содержать толькб шесть символов. Тип данных VARCHAR2 лучше всегоподходит для столбцов, где нельзя точно предсказать длину текста в каждой за-писи. Это относится к большинству текстовых столбцов, хранимых в базе дан-ных — именам, описаниям и т. д.

ПримечаниеПочему тип назван VARCHAR2, а не VARCHAR?Хороший вопрос.Тип данных с названием VARCHAR тоже существует, и в текущихверсиях Oracle он полностью аналогичен VARCHAR2. Однако корпо-рация Oracle заявляет, что в будущем свойства столбцов VARCHARмогут быть изменены, и пока неизвестно, что будет представлятьсобой измененный VARCHAR. Поэтому вам следует всегда указы-вать полное название — VARCHAR2.

Сохранение и выборка данных: основы 33

Чтобы поработать с этими двумя типами данных, введите в SQL*Plus следу-ющий код:

CREATE TABLE plsqll01_test_2 (

name VARCHAR2 (.20) ,gender CHAR(l)

INSERT INTO plsqll01_test_2 VALUES ('George1, 'M');

INSERT INTO plsql!01_test_2 VALUES ('Jane1, 'F');

SELECT * FROM plsq!101_test_2;

DROP TABLE plsql!01_test_2;

Когда вы закончите, экран SQL*Plus должен выглядеть примерно так, какпоказано на рис. 2.6.

яп-ш£йе Edit Search Option* Help

SQL> CREATE TABLE plsq!1B1_test 2 (2 name UARCHAR2(20),3 gender CHAR(1)* )5 ;

Table created.

SQL>SQL> INSERT INTO plsq!101_test_2 UALUES ('George

1, 'M');

' • • • - ' - , : ':•'•••' " •' • , "1 row created.

SQL> INSERT INTO plsqliei_test_2 UALUES ('Jane1. 'F');

1 row created. у

SQL>

SQL> SELECT « FROM plsqllB1_test_2;

NflHE G

George ИJane F

SQL>SQL> DROP TABLE plsq!101_test_2;

Table dropped.

SQL>

Рис. 2.6. Вставка записей со значениями типа CHAR и VARCHAR2

34 Глава 2

ПримечаниеВозможно, вы заметили, что текст в команде INSERT окружен оди-ночными кавычками. Это не зависит от типа SQL-команды, которуювы используете. Текстовые данные в отличие от чисел всегда окру-жаются одиночными кавычками.

Существует еще один тип данных, предназначенный для хранения текста:LONG. Он позволяет хранить до 2 147 483 647 символов (2 гигабайта) текста.Однако за такую огромную емкость приходится платить: тип LONG имеет мно-го ограничений на способы использования. Описание работы с этим типомданных выходит за рамки данной книги, но если он вам потребуется, все необ-ходимые сведения можно найти в электронной документации Oracle.

,• Ш ПримечаниеНа компьютерном языке единица текста называется строкой(string), что служит более коротким эквивалентом выражения"строка символов". Люди, много работающие сданными, часто ис-пользуют термины "текст"и "строка"как взаимозаменяемые. Дру-гим распространенным термином является ASCII, сокращение отAmerican Standard Code for Information Interchange (американскийстандартный код для обмена информацией). ASCII представляетсобой согласованный стандарт, в котором определены числовыепредставления букв английского алфавита, цифр и специальныхсимволов клавиатуры, а также несколько специальных кодов дляуправления устройствами типа принтеров. Этот стандарт предназ-начен для приведения информации к "общемузнаменателю", что-бы обеспечить ее перенос между компьютерами. Используяаббревиатуру "ASCII", обычно хотят подчеркнуть, что файл содер-жит только текст — без форматирования, отступов, шрифтовых вы-делений или подчеркивания. Например, файл Microsoft Word(с расширением .doc) не является ASCII-файлом, но если восполь-зоваться командой File \ Save As для его сохранения в формате TextOnly, то результатом будет файл в кодировке ASCII. (Это справед-ливо только в том случае, если документ содержит английскийтекст. — Прим, пер.) ASCII-файлы могут быть открыты в любом тек-стовом процессоре или редакторе.

Как Oracle сохраняет числаДля определения столбцов, в которых будут храниться только числа, исполь-

зуется тип данных NUMBER.-При определении столбца типа NUMBER такжеуказывается, сколько цифр должен хранить столбец. Эта спецификация можетсостоять из двух частей: количества цифр до десятичной точки и количествацифр после десятичной точки.

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

CREATE TABLE plsq!101_product (

product_name VARCHAR2(25),product_price NUMBER(4,2)

Сохранение и выборка данных: основы 35

INSERT INTO plsql!01_product VALUES ('Product Name 1', 1);INSERT INTO plsql!01_product VALUES ('Product Name 2', 2.5);

INSERT INTO plsql!01_product VALUES ('Product Name 3', 50.75);INSERT INTO plsql!01_product VALUES ('Product Name 4', 99.99);

SELECT * FROM plsql!01_product;

DROP TABLE plsql!01_product;

Когда вы закончите, экран должен выглядеть так, как показано на рис. 2.7.

Ж Oiacle SQL'PlusFile £dit Search Options Help

SQL> CREATE TABLE plsqllB1_product (2 product_nane UARCHAR2(25),3 product_price NUMBER (it, 2)* )5 ;

Table created.

SQL>SQL> INSERT INTO plsql101_prodiict UflLUES ('Product Name 1', 1);

1 row created.

SQL> INSERT INTO plsqllByproduct UflLUES ('Product Name 2 ' , 2.5);

1 row created.

SQL> INSERT INTO plsqll81_product UflLUES ('Product Name 3', 50.75);

1 row created.

SQL> INSERT INTO plsqll81_product UflLUES ('Product Name V , 99.99);

1 row created.

SQL>SQL> SELECT * FROM plsqll Byproduct;

PRODUCT NAME PRODUCT PRICE

Product Name 1Product; Name 2Product Name 3Product Name Ц

SQL>SQL> DROP TABLE plsqll81_product;

Table dropped.

SQL>

12.5

50.7599.99

Рис. 2.7. Вставка записей со значениями типа NUMBER

36 Глава 2

Как вы могли заметить, объявление типа данных NUMBER имеет следую-щий синтаксис:

'Ы\]МВЕЩобщее_число_цифр, число_цифр_после_десяттной_точки)

Тип данных NUMBER позволяет хранить поистине огромные числа: наибо-льшее значение составляет 999999999999999999999999999 999 999 999 990 000000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000000 000 000 000 000 000 000. Число цифр после десятичной точки может дости-гать 127. Такой диапазон значений рассчитан на промышленное применение иявляется одной из черт, отличающих серьезные базы данных, такие, как Oracle,от офисных продуктов, к которым относятся электронные таблицы.

Как Oracle сохраняет датыДаты представляют для компьютеров интересную проблему. Рассмотрим,

например, такой список дат:

January 15, 2002February 15, 2002March 15,2002

Этот список можно хранить в текстовом столбце, но даты не будут правиль-но отсортированы, поскольку текстовые столбцы сортируются слева направо, абуква "F", с которой начинается слово February, стоит перед "J" (January) и"М" (March). Чтобы обойти эту проблему, месяцы можно представлять числа-ми, а не названиями. В результате мы получим следующий список:

01-15-200202-15-200203-15-2002

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

•;v 01-15-201002-15-200503-15-2000

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

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

2010-01-152005-02-152000-03-15

С точки зрения сортировки здесь все хорошо. При считывании символовслева направо отсортированная версия списка примет вид:

Сохранение и выборка данных: основы 37

2000-03-152005-02-152010-01-15 ;/

Это изящное решение, если все, что вам нужно делать с датами, — это сорти-ровать их в хронологическом порядке. Однако во многих ситуациях даты требу-ется не только сортировать. В бухгалтерии хотят знать, какие дебиторскиезадолженности должны быть погашены в течение следующих 15 дней, и ктопросрочил оплату своего счета более чем на 30 дней. Руководству компаниитребуются сведения об изменениях продаж за данный период по сравнению стем же периодом прошлого года. Менеджеры необходимы данные о том, сколь-ко времени займет проект, если удвоить все сроки. Работа такого типа требуетсравнения двух дат и подсчета числа дней (недель, месяцев или лет), которые ихразделяют. Это называется вычислениями с датами (date math).

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

Такой способ счета дней существует давно: это юлианский календарь. Когдасистема использует юлианский календарь, начальному дню присваивается но-мер 1, следующий за ним называется днем 2 и т. д. Поскольку каждый последу-ющий день увеличивает счетчик на единицу, календарь такого типа идеальноподходит для вычислений с датами. Oracle поддерживает юлианский кален-дарь, где начальной датой является 1 января 4712 г. до н.э. Преобразование датмежду визуальным форматом (например, '08-MAY-2004') и его юлианским эк-вивалентом производится автоматически. Мы просто вводим даты в привыч-ном текстовом представлении, затем Oracle преобразует их в свой внутренний,юлианский, формат, а при выборке этих дат из таблицы они снова отображают-ся виде дней, месяцев и лет. Нам никогда не потребуется просматривать даты вюлианском формате.

Чтобы получить представление о том, как происходит работа с датами вOracle, введите следующие команды:

CREATE TABLE plsq!101_purchase (product_name VARCHAR2 (25) ,

product_price NUMBER(4,2),

purchase_date DATE\

INSERT INTO plsql!01_purchase VALUES

('Product Name 1', 1, '5-NOV-OO ' ) ;

INSERT INTO plsql!01_purchase VALUES

('Product Name 2', 2.5, ' 29-JUN-01 ' ) ;INSERT INTO plsq!101_purchase VALUES

('Product Name 3', 50.75, ' 10-DEC-02 ' )

38 Глава 2

INSERT INTO plsql!01_purchase VALUES('Product Name 4', 99.99, '31-AUG-03');

SELECT * FROM plsqllOljpurchase;

После выполнения этого упражнения экран должен выглядеть так, как по-казано на рис. 2.8.

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

* Oiacle SQL'Plus

File Edit Search Options HelpSQL> CREflTE TftBLE plsql101_purchase (

2 product_name UARCHAR2(25),

3 product_price NUMBtR(4,2),4 purchase_date ОПТЕ5 )6 ;

Table created.

SQL>SQL> INSERT INTO plsqll01_purchase UflLUES2 ('Product Name 1', 1, 'S-NOU-ee

1);

1 row created.

SQL> INSERT INTO plsqll81_purchase UftLUES2 ('Product Name 2', 2.5, '29-JUN-B1' );

1 row created.

SQL> INSERT INTO plsqllB1_purchase UflLUES2 ('Product Name 3', SB.75, 'IB-DEC-eZ

1);

1 row created.

SQL> INSERT INTO plsql1B1_purcHase UflLUES2 ('Product Name V, 99.99, '31-1ШС-вЗ');

1 row created.

SQL>SQL> SELECT * FROM plsqllB1_purcnase;

PRODUCT НЙМЕ PRODUCT PRICE PURCHASE

Product Name 1Product Name 2Product Name 3Product Name 4

SQL> |

1 05-NOU-002.5 29-JUN-B1

58.75 18-DEC-B299.99 31-flUG-B3

Рис. 2.8. Вставка записей со значениями типа DATE

Сохранение и выборка данных: основы 39

Кроме приспособленности к вычислениям, юлианский календарь имеет идругие достоинства. Например, если кто-то попытается вставить дату 29 февра-ля 2002 г. в столбец с датами, Oracle не позволит это сделать, поскольку 2002 г.не является високосным, а следовательно, не содержит 29 дней в феврале. ТипDATE допускает также хранение времени. Время хранится в виде десятичнойдроби, следующей за целым числом, представляющим дату (или нулем, есликомпонент с датой отсутствует). Например, если значение юлианской датыравно 54321, то полдень этого дня будет представлен как 54321.5 (.5 показывает,что прошла половина дня). Время 6:00 того же дня будет храниться как54321.25, а 18:00 — как 54321.75. Другим значениям времени будут соответство-вать не столь круглые числа. Например, чтобы представить время 15:16, нужнодобавить .636111111 к юлианскому номеру дня. (В главе 6 у вас будет возмож-ность с этим поэкспериментировать.)

Определение структуры таблицыСоздавая свою собственную таблицу, вы знаете ее структуру... до поры до

времени. Потом вам придется заниматься другими вещами, и вы забудете по-дробности того, что делали раньше. Если же таблицу создал кто-то другой, вывообще не имеете представления об ее структуре. Следовательно, нужно иметьвозможность определять структуру существующей таблицы. Вероятно, вас неочень удивит известие о том, что в Oracle есть команда, предназначенная имен-но для этой цели. Она называется DESCRIBE (сокращенно — DESC) и имеетследующий синтаксис:

DESC имя_таблицы

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

Чтобы увидеть, как работает команда DESC, введите следующий код:

DESC plsql!01_purchase

Экран с результатами показан на рис. 2.9. Выходными данными командыDESC являются три столбца: Name, Null? и Туре. Под заголовком Name пере-числены имена всех столбцов таблицы в порядке их появления в этой таблице.Назначение столбца Null? будет объяснено в следующем разделе, а столбецТуре показывает тип данных и длину каждого из столбцов.

Столбцы NULL и NOT NULLПри разработке таблиц для хранения информации определенного назначе-

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

40 Глава 2

С Oioclt! SQL-Plus ИИ И

Ete Ed» Search Options Цв1рSQL> DESC plsqliei_purchase

Name Null? Type

PRODUCT_NA№ UflRCHflR2(25)PRODUCT_PRICE NUMBER(4,2)PURCHflSE_DOTE DATE

SQL>

Рис. 2.9. Результаты команды DESCRIBE

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

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

ИмяФамилияДата приема на работуДолжностьОтделНачальникЗарплатаДата рожденияСтраховой планДобавочный телефон

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

Сохранение и выборка данных: основы 41

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

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

Указать, какие столбцы являются обязательными, можно в командеCREATE TABLE. (Можно также изменить существующую таблицу; это будетрассмотрено в главе 3.) В этой команде столбец объявляется как обязательныйпутем добавления слов "NOT NULL" после его имени и длины. Чтобы увидеть,как это работает, введите в SQL*Plus следующие команды:

DROP TABLE plsql!01_purchase;

CREATE TABLE plsql!01_purchase (product_name VARCHAR2 (25) NOT NULL, r';>product_price NUMBER(4,2) NOT NULL, .;.,• .,purchase_date DATE

i

В результате экран будет выглядеть так, как показано на рис. 2.10. Чтобы ,проверить, к чему привело объявление столбца как NOT NULL, вы должныуметь вставлять запись, не содержащую никаких данных. Это и будет первойтемой следующего раздела.

* Oracle SQL-Plus

File Edit Search Options Help

SQL> DROP TABLE plsql101_purchase;

Table dropped.

SQL>SQL> СВЕЙТЕ TABLE plsq11B1_purchase (

2 product_nane UftRCHflR2(25) NOT NULL,3 product_price NUHBER(I»,2) NOT NULL,i» purchase_date DATE5 )6 ;

Table created.

SQL>

-dJ

НГ-Ш

Рис. 2.10. Создание таблицы со столбцами NOT NULL

42 Глава 2

Вставка данных —дополнительные приемы

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

Как вставлять записи с null-значениямиВы уже знаете, что null-значение соответствует пустому атрибуту — напри-

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

Первый заключается в том, чтобы использовать в операторе INSERT слово"NULL" вместо реального значения. Например, в последней созданной вамитаблице (PLSQL101_PURCHASE) столбцы с названием и ценой товара явля-ются обязательными, тогда как столбец с датой покупки — нет. Следовательно,можно вставить запись только с первыми двумя атрибутами:

INSERT INTO plsql!01_purchase VALUES ('Product Name 1', 1, NULL).;

SELECT * FROM plsqll01_purchase;

Результат выполнения этих команд показан на рис. 2.11. Обратите внима-ние: когда SQL*Plus выводит запись в ответ на команду SELECT, третий стол-бец остается пустым.

;*. Oracle SQL-Plus

File Edit Search Options Help .SQL> INSERT INTO plsql1B1_purchase UALUES ('Product Name 1', 1, NULL);

1 row created.

SQL>SQL> SELECT » FROM plsqllB1_purchase;

PRODUCT_NflME PRODUCTJMUCE PURCHftSE_

Product Name 1 1'

SQL>

Рис. 2.11. Вставка записей с null-значениями — первый способ

Сохранение и выборка данных: основы 43

Теперь, когда вы знаете, как вставлять записи с null-значениями, пора про-верить, действуют ли установки NOT NULL, выполненные для первых двухстолбцов таблицы. Как это сделать? Очевидно, путем вставки записи, не содер-жащей значений в одном или обоих обязательных столбцах. Для полной уве-ренности следует протестировать все три варианта: когда отсутствует названиетовара, цена товара и обе величины сразу. Введите следующие команды:

INSERT INTO plsql!01_purchase VALUES

(NULL, 2.5, '29-JUN-01');

INSERT INTO plsql!01_purchase VALUES

('Product Name 3', NULL, '10-DEC-02');

INSERT INTO plsql!01_purchase VALUES

(NULL, NULL, '31-AUG-03');

SELECT * FROM plsqll01_purchase;

Результаты должны быть примерно такими, как на рис. 2.12. Каждая изкоманд INSERT приводит к выдаче сообщения об ошибке, которое в характер-ной дружеской манере напоминает, что нельзя вставлять null-значения в столб-цы, созданные как NOT NULL.

Ж Oracle SQl'Plus

fife £dit Search Qplions H*SQL> INSERT INTO plsqll01^purchase UALUES ^ jj

2 (NULL, 2.5, '29-JUN-ei'); -JINSERT INTO plsqll01_purchase UALUES

w

ERROR at line 1:ORO-01itOO: cannot insert NULL into' (•lPLSQLiai"."PLsqL101_PURCHflSE".llPRODUCT_NAME")

SQL> INSERT INTO plsqll01_purchase UALUES2 ('Product Name 3 1 , null, 'lO-DEC-BZ1);

INSERT INTO plsqll01 purchase UALUES

*ERROR at line 1:OHfl-01!»00: cannot insert NULL into (11PLSQL181'1."PLSQL101_PURCHASE".>1PRODUCTJ>RICE")

SQL> INSERT INTO plsqll01 purchase UALUES2 (NULL, NULL, 'ai-flUG-BS1);

INSERT INTO plsqllei_purchase UflLUES

*ERROR at line 1:ORft-OUtOO: cannot insert NULL into ("PLSQL101"."PLSQLiei_PURCHASE".lIPROOUCT_HftllE")

• : . - • ' . . / , 'SQL>SQL> SELECT « FROM plsq11И1 purchase;

PRODUCT_NAME PRODUCT_PRICE PURCHASE_

Product Name 1 1

SQL> |

Рис. 2.12. Результаты вставки null-значений в столбцы NOT NULL,

44 Глава 2

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

В этом способе используется разновидность синтаксиса команды INSERT,где нужно явно указывать имена всех столбцов, в которые вставляются данные.Во всех предыдущих командах INSERT вы не объявляли, в какие столбцы на-мереваетесь вставить значения, а просто указывали эти значения. Когда коман-да INSERT записана таким образом, Oracle предполагает, что вы вставляетезначения во все столбцы, присутствующие в таблице, и что эти значения указа-ны в порядке расположения столбцов в таблице. Явно указывая, какие столбцыи в каком порядке заполняются, вы отменяете оба предположения и получаетевозможность пропускать любые столбцы.

Чтобы увидеть, как это делается, введите следующие команды:

INSERT INTO plsql!01_purchase (product_name, product_price)

VALUES ('Product Name 2', 2.5);INSERT INTO plsql!01_purchase (product_name, product_price)

VALUES ('Product Name 3', 50.75);INSERT INTO plsql!01_purchase (product_price, product_name)

VALUES (99.99, 'ProductName 4');

SELECT * FROM plsqll01_purchase;

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

,+ Oiacle SQL'Plus

0e fid» £e«ch Qptiora HelpQL> INSERT INTO plsqll

2 UflLUES С Product Name 2 ' , 2.5);

1 row created.

SQL> INSERT INTO plsq!1B1_purchase (product_nane, product_prlce) jj

SQL> INSERT INTO plsqll 6"1_purcnase (product папе, product price)2 UflLUES ('Product Name 3

1, 58.75)Г

1 row created.

SQL> INSERT INTO plsq!101_purchase (product_price, product_nane)2 UflLUES (99.99. 'Product Name 4');

•' ' • - . • • . . ' • ' - • . . , , ' • • • • • .1 row created.

SQL>SQL> SELECT * FROH plsqllB1_purchase;

PRODUCT_NflME PRODUCT_PRICE PURCHASE.

Product Name 1 1Product Name 2 2.5Product Name 3 58.75Product Name i» 99.99

SQL>

JJJ

Рис. 2.13. Вставка записей с null-значениями — второй способ

Сохранение и выборка данных; основы 45

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

Как вставлять данные с апострофамиРано или поздно вам наверняка потребуется вставлять записи с текстом, со-

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

' • * ' ' - ' < • - . • ' . :

INSERT INTO plsql!01_purchase VALUES

('Fifth Product's Name', 25, '05-MAY-03');

В ответ Oracle выведет сообщение об ошибке, показанное на рис. 2.14.

* Otacle SQL'Plu

£!е ЕА Search Qptiora Help .'•••

SQL> INSERT INTO plsq!101_purchase UflLUES2 ('Fifth Product1s Name', 25, '05-MftV-03');

ERROR:ORfl-61756: quoted string not properly terminated

SQL>

ИИ 13!

Рис. 2.14. Попытка вставить текстовую строку, содержащую апостроф

Ясно, что такой подход не работает. Чтобы получить желаемый результат,вы должны сделать две вещи: выдать команду SET SCAN OFF перед INSERT ипоместить два апострофа в том месте текстовой строки, где должен находитьсяодиночный апостроф. Таким образом, последовательность команд будет иметьследующий вид: ,SET SCAN OFF

INSERT INTO plsql!01_purchase VALUES('Fifth Product''s Name', 25, '05-МАУ-ОЗ');

SET SCAN ON

Введите эти команды, а затем проверьте результат их выполнения:

SELECT * FROM plsqll01_purchase;

46 Глава 2

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

Ж Oracle SQL'Plu

File Edit Seaich flptions HelpSQL> SET SCftN OFFSQL>SQL> INSERT INTO plsqliai_purchase UftLUES

2 ('Fifth Product1's Name', 25, 'OS-MAY-OS');

1 row created.

SQL>SQL> SET SCAN ONSQL>SQL> SELECT * FROM plsq!1B1_purchase;

PRODUCT NAME PRODUCT PRICE PURCHASE

Product Name 1 1

Product Name 2 2.5

Product Name 3 58.75

Product Name 4 99.99

Fifth Product's Name 25 85-MflV-03

SQL>

jJJ

Рис. 2.15. Результат применения правильного способа вставки текстас апострофом

• • -

Просмотр данных —дополнительные приемы

Теперь, когда вы умеете выполнять простые выборки с помощью командыSELECT, настало время изучить ряд более сложных способов просмотра дан-ных в таблице. Из этого раздела вы узнаете, как выбирать из таблицы опреде-ленные столбцы, изменять порядок отображения выбранных столбцов,выполнять вычисления с данными из таблицы, соединять текстовые строки иизменять имена столбцов. Готовы? Тогда начнем.

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

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

"

.1'*;.., У У SELECT product_name FROM plsql!01_purchase;

Сохранение и выборка данных: основы 47

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

* Oracle SQL-Plus

File Edit Search flptkms HelpSQL> SELECT product_narae FROM plsqll01_purchase;

PRODUCT NftME

Product Name 1Product Name 2Product Name 3Product Nane 4Fifth Product's Nane

SQL>

.dJ

Рис. 2.16. Выбор заданных столбцов

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

SELECT product_name, purchase_date FROM plsq!101_purchase;

Изменение порядка столбцовЗная, как выбираются определенные столбцы из таблицы, очень легко изме-

нить последовательность их вывода. В команде SELECT нужно просто поста-вить столбцы в том порядке, в котором вы хотите их видеть.

Например, чтобы просмотреть столбцы таблицы PLSQL101_PURCHASE вобратном порядке, введите такую команду:

SELECT purchase_date, product_price, product_nameFROM plsql!01_purchase;

Результат должен быть таким, какнарис. 2.17. Чтобы лучше освоить эту тех-нику, выберите записи из таблицы PLSQL101_PURCHASE несколько раз,по-разному располагая столбцы.

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

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

3 Зак. 725

48 Глава 2

НИИfte £<й Search Options Help : ;• ' , ; • . : ; :;:

SQL> SELECT purchase_date, product_price, product_nane2 FROM plsql1B1~purchase;

PURCHASE. PRODUCT_PRICE PRODUCTJfflME

и

05-ИАУ-03

SQL>

<| J

1 Product Name 12.5 Product Name 2

58.75 Product Name Э99.99 Product Name 4

25 Fifth Product's Name

,...*

Рис. 2.17. Столбцы, показанные в заданном порядке

Допустим, вы хотите узнать, каковы будут цены из таблицыPLSQL101_PURCHASE после их увеличения на 15%. Введите следующуюкоманду:

SELECT product_name, product_price * 1.15 FROM plsqll01_purijhase;

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

* Oracle SQL-Plus НИИ!file Edit Search Option? Help

SQL> SELECT product_nane, product_price * 1.15 FROM plsql101_purchase;

PRODUCT NflME PRODUCT PRICE*1.15

Product Name 1Product Нам 2Product Нам 3Product Nam 4Fifth Product's Nan

SQL>

1.152.875

58.3625114.9885

28.75

Рис. 2.18. Математические операции: увеличение значения на 15% .

Математические операторыНа техническом языке математические символы, обозначающие операции,

называются операторами (operators). Например, знаки плюса и минуса — этооператоры. Oracle поддерживает четыре стандартные арифметические опера-ции — сложение, вычитание, умножение и деление. Умножение обозначаетсязвездочкой (*). Для деления используется символ /, а для сложения и вычита-

Сохранение и выборка данных: основы 49

• • • ' . . . - . - - . . . - . ( . . - , • , - . - : . . - , . , , ... - . , • f <,..*-, Wc* -

ния — символы + и -, соответственно. Чтобы рассмотреть пример, имеющийотношение к реальной жизни, нам потребуется новая таблица с двумя число-выми столбцами. Приведенные ниже команды создают такую таблицу, а такжедемонстрируют использование математических операторов.

DROP TABLE plsql!01_purchase; :

,

CREATE TABLE plsql!01_purchase (

product_name VARCHAR2(25) ,

product_price NUMBER(4,2),

sales_tax NUMBER(4,2),

purchase_date DATE,

salesperson VARCHAR2(3)

INSERT INTO plsql!01_purchase VALUES('Product Name I

1, 1, .08, '5-NOV-OO', 'AB');

INSERT INTO plsql!01_purchase VALUES('Product Name 2', 2.5, .21, '29-JUN-01', ',CD');

INSERT INTO plsql!01_purchase VALUES('Product Name 3', 50.75, 4.19, 40-DEC-02

1, 'EF');

INSERT INTO plsql!01_purchase VALUES('Product Name 4', 99.99, 8.25, '31-AUG-Q3

1, 'GH');

SELECT product_name, product_price + sales_ta'x

FROM plsqllOljpurchase;SELECT product name, 100 - product_price

FROM plsqll01_purchase;SELECT product_name, sales_tax / product_price ..

FROM plsqllOljpurchase;

Результаты трех команд SELECT должны выглядеть примерно так, как по-казано на рис. 2.19.

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

Что такое выражение?В Oracle термин выражение (expression) имеет различные значения. В нашем

случае он обозначает часть команды, состоящую из одного и более имен столб-цов, NULL, введенных вами значений (таких, как" 100" в предыдущей последо-вательности команд, которые также называются константами, поскольку онификсированы), или любой комбинации этих членов, соединенных математи-ческими операторами. Рассмотрим в качестве примера следующую команду:

SELECT product_name, product_price * 2 + 10 FROM plsqllOljpurchase;

50 Глава 2

* Oracle SQL'Plus 1-IOiX

File Edit Search Options HelpSQL> SELECT product name, product_price + sales_tax FROM plsql181_purchase; _±j

PRODUCT НЙМЕ PRODUCT PRICE+SflLES TUX

Product Name 1Product Name 2Product Name 3Product Name 4

1.882.71

51». 94

SQL> SELECT product naroe, 180 - product_price FROM plsql1B1_purchase;

PRODUCT NflME 180-PRODUCT PRICE

Product Name 1

Product Name 2

Product Name 3

Product Name 4

9997.549.25

.81

SQL> SELECT productjnane, sales_tax / product_price FROM plsql181_purchase;

PRODUCT NflME SftLES ТИХ/PRODUCT PRICE

Product Name 1Product Name 2Product Name 3Product Name 4

SQL> |

.88

.084

.082561576

.882588251

Рис. 2.19. Команды SELECT с различными математическими операторами

Она содержит два выражения: PRODUCT_NAME и PRODUCT_PRICE*2+ 10.

У вас может возникнуть вопрос: "Как Oracle обрабатывает выражения, со-держащие более одного математического оператора?" Это переводит нас к темеприоритета операторов (operator precedence).

Приоритет операторовКогда выражение содержит бол ее одного математического оператора, Oracle

применяет правила, определяющие порядок выполнения операций. Умноже-ние, деление и любые операции в скобках выполняются в первую очередь, сле-ва направо. Затем выполняются сложение и вычитание, также слева направо.

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

Для большей ясности последний пример можно записать таким образом:

SELECT product_name, (product_price * 2) + 10 FROM plsql!01_purchase;

Сохранение и выборка данных: основы 51

Соединение двух и более частей текстаПри работе с базами данных часто возникают ситуации, когда желательно

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

Чтобы указать на необходимость соединения двух столбцов в оператореSELECT, нужно поместить между их именами две вертикальные черты (||). На-пример, следующая команда производит конкатенацию содержимого столб-цов product_name и salesperson:

SELECT product_name || salesperson FROM plsq!101_purchase;

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

SELECT product_name I | ' was sold by ' I | salesperson

FROM plsql!01_purchase;

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

(literal). Это означает, что он будет воспроизводиться символ за символом, а неинтерпретироваться как, имя таблицы, столбца или другого объекта.

* Oiacle SQL-Plus•«•• •••«•• •••••••••••••• •••••«••••••«••••••••••••••••••••••••••••••РНВВНИМННВ

File Edit Search Options HelpSQL> SELECT product_name || ' was sold by ' | | salesperson

2 FROM plsq!101 purchase;

PRODUCT _NftME | | ' U A S S O L D B V 1 | | SALESPERSON

Product Name 1 was sold by ftBProduct Name 2 was sold by CDProduct Name 3 uas sold by EFProduct Name 4 Mas sold by CH

ЯНЕ31

d

. ; . - ' - .

SQL>I

Рис. 2.20. Использование конкатенации строк и текстового литералав команде SELECT

52 Глава 2

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

совсем "отбился от рук". По умолчанию заголовками столбцов служат их имена.Однако при выполнении оператора SELECT, в котором используется конкате-нация столбцов, в качестве заголовка отображается все выражение. Обычно этовыглядит непривлекательно и редко приносит пользу. SQL позволяет опреде-лить, что будет помещено на вершине столбца, выбранного операторомSELECT. Все, что для этого нужно, — ввести после имени столбца (или выра-жения) текст, который вы хотите видеть в качестве заголовка.

Чтобы понять, как это делается на практике, введите следующий вариантпредыдущей команды SELECT:

SELECT product_name || ' was sold by ' I I salesperson SOLDBY

FROM plsql!01_purchase;

Имя-заменитель, которое вы указываете для столбца, называется псевдони-мом столбца (alias). В данном случае псевдоним SOLDBY удобнее для чтения,но все равно выглядит несколько неуклюже. Если заключить псевдоним вдвойные кавычки, в нем можно будет использовать пробелы и буквы нижнегорегистра. (Двойные кавычки необходимы для того, чтобы Oracle не пыталсяинтерпретировать псевдоним как имя столбца, подлежащего выборке.) Чтобыувидеть, как это работает, введите следующую команду:

SELECT product_name || ' was sold by ' || salesperson "Sold By"

FROM plsql!01_purchase;

В результате экран должен выглядеть так, как показано на рис. 2.21.

A Oracle SQL-Plus НщЦЗ

file Е* Search Qptions HelpSQL> SELECT product_nane 11 • was sold by ' || salesperson "Sold By1'

2 FROM plsq!1B1_purchase;

Sold By

Product Mane 1 was sold by ftBProduct Name 2 was sold by CDProduct Name 3 was sold by EFProduct Name 4 was sold by GH

SQL>

Рис. 2.21. Изменение заголовка столбца с помощью псевдонима столбца

ИтогиЭта глава дала вам понимание основ SQL. После короткого упражнения, где де-

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

Сохранение и выборка данных: основы 53

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

Определенное количество слов зарезервировано и не может использоватьсяв качестве имен таблиц или столбцов. В частности, к ним относятся команды(например, CREATE) и имена объектов (например, ROW). Зарезервированныхслов слишком много, и запомнить их все большинству людей не под силу. Слу-чайно употребив одно из них, вы сразу узнаете об этом, поскольку вместо нор-мального завершения команды Oracle выдаст сообщение об ошибке с фразой"Invalid table name" или "Invalid column name". Если проблема связана с именемтаблицы, добавьте в начале имени аббревиатуру, обозначающую систему, ча-стью которой является таблица (например, AP_ADMIN вместо ADMIN). Еслипроблема связана с именем столбца, добавьте к имени одно-два слова, чтобылучше описать содержимое столбца. ,-,

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

Имена таблиц лучше всего записывать в единственном, а не множественномчисле: например, таблицу служащих следует назвать EMPLOYEE, а неEMPLOYEES. Кроме того, не нужно включать в имя таблицы слова TABLE иDATA, поскольку все, о чем они сообщают, следует из самого факта работы стаблицей. При создании таблицы вы должны указать тип данных и длинукаж-дого столбца. Таблицы Oracle могут хранить любые разновидности данных —текст, числа, даты, изображения, звуковые файлы и т. д. Каждый тип данныхимеет определенный набор свойств. Самые распространенные типы данныхтаблицы — это текст, числа и даты.

Текстовый столбец может содержать буквы, цифры, пробелы и специаль-ные символы — все, что можно ввести с клавиатуры. Когда в текстовый столбецвводится число, оно также рассматривается как текст. Числа в текстовых столб-цах невозможно складывать, усреднять или выполнять над ними какие-либодругие математические операции. В текстовые столбцы обычно помещаютсячисла, содержащие нецифровые символы — знаки математических операций,буквенные символы или пробелы. Широко известными примерами текстовыхзначений, содержащих большое количество чисел, являются телефонные но-мера, номера социального обеспечения и счетов. Если числовое значение ни-когда не предполагается использовать в математических операциях, оноявляется хорошим кандидатом на помещение в текстовый столбец.

Существуют два основных типа текстовых столбцов: с фиксированной и^фзременной длиной. Для создания столбца фиксированной длины нужно указатьв команде CREATE TABLE тип данных CHAR. Длина текста в столбцах CHARвсегда равна длине, указанной в объявлении столбца. Более короткие данные

54 Глава 2

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

Большинство текстовых столбцов будут содержать данные переменной дли-ны, и здесь следует использовать тип VARCHAR2. Если длина текста, которыйвы намереваетесь хранить в столбце, превышает 2000 символов (это максимумдля типа VARCHAR2), можно воспользоваться типом LONG, который позво-ляет хранить до двух миллиардов символов в одном поле.

Текстовое значение обычно называется строкой. В SQL-командах строкивсегда заключаются в одиночные кавычки. Простой текст, содержащий толькоте символы, которые можно найти на клавиатуре (без символов форматирова-ния, вставляемых текстовыми процессорами и электронными таблицами), ча-сто называется ASCJI-текстом. (ASCII — это сокращение от American StandardCode for Information Interchange.) Стандарт ASCII предназначен для приведе-ния информации к "общему знаменателю", чтобы обеспечить ее перенос междукомпьютерами.

Для числовых столбцов в отличие от текстовых Oracle предлагает всего одинбазовый тип данных с названием NUMBER. При создании числового столбцавы просто указываете максимальное количество цифр, которое он может со-держать, вместе с требуемым количеством цифр после запятой. Наибольшеечисло, которое можно хранить, составляет 999 999 999 999 999 999 999 999 999999 999 999 990 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000000 000 000 000 000 000 000 000 000 000 000 000.

Для хранения даты и времени Oracle предоставляет тип данных DATE. Зна-чения дат (которые, как и любой текст, заключаются в одиночные кавычки)при сохранении преобразуются в юлианские даты. Преобразование дат междувизуальным форматом (например, '08-MAY-2004') и его юлианским эквива-лентом производится автоматически. Мы просто вводим даты в привычномтекстовом представлении, затем Oracle преобразует их в свой внутренний,юлианский, формат, а при выборке этих дат из таблицы они снова будут ото-бражены в виде дней, месяцев и лет. Нам никогда не потребуется просматри-вать даты в юлианском формате.

Тип данных DATE можно использовать для вычислений с датами. Напри-мер, чтобы получить дату, отстоящую на неделю от некоторой известной даты,нужно просто добавить 7 к этой известной дате. Oracle также проверяет досто-верность дат; например, если кто-то попытается вставить дату 29 февраля 2002г. в столбец с датами, то Oracle не позволит это сделать, поскольку 2002 г. не яв-ляется високосным, а следовательно, не содержит 29 дней в феврале.

Время хранится в виде десятичной дроби, показывающей, какая часть дняпрошла к этому моменту. Например, если некоторому дню соответствует юли-анская дата 33333, то время 18:00 этого дня будет сохранено как 33333.75 (.75показывает, что к 18:00 прошло 75% дня).

После изучения самых распространенных типов данных Oracle вы познако-мились с тем, как использовать команду DESC для просмотра структуры суще-ствующей таблицы. Вы также узнали, что в команде CREATE TABLE можноуказать спецификацию NOT NULL для любого столбца (или всех столбцов), врезультате чего вставка или обновление записей будут возможны только приуказании значений для этих столбцов.

Сохранение и выборка данных: основы 55

При добавлении или изменении данных в таблицах, столбцы которых допу-скают null-значения, можно избежать ввода значения в столбец, если указатьNULL в том месте SQL-оператора, где должно находиться значение столбца.Пропустить столбцы в команде INSERT можно и другим способом — явно пе-речислить все заполняемые столбцы, не указывая те, в которые не предполага-ется вводить данные. Чтобы вставить данные с апострофами при помощипрограммы SQL*Plus, предварите команду INSERT командой SET SCAN OFF.Закончив вставку данных с апострофами, введите команду SET SCAN ON, что-бы вернуть систему в нормальный режим.

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

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

Если вы хотите сложить не числа, а текстовые строки (иначе говоря, выпол-нить конкатенацию двух текстовых столбцов), поместите между именамистолбцов в операторе SELECT две вертикальные черты (||). Чтобы разделить двечасти текста пробелом, поместите между именами столбцов комбинацию ||'' ||.В результате этих действий заголовок столбца может оказаться слишком длин-ным и неудобным для чтения. В таком случае следует присвоить столбцу псев-доним, указав его после выражения, содержащего оператор конкатенации.

Для одной главы это довольно заметный прогресс. Продолжайте читать, ивы узнаете еще больше!

Вопросы1. Что такое тип данных?

A. Информация, жестко закодированная в SQL-команде• • • • ' Ц •

B. Метод, используемый Oracle для хранения дат

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

D. Разновидность компьютерного терминала, использовавшегося допоявления персональных компьютеров и подключавшегося к мэй-нфреймам.

56 Глава2

2. Каков правильный синтаксис создания таблицы с двумя столбцами?

A. CREATE TABLE имя_таблицы

имя_столбца_1 тип_данных,

имя_столбца_2 тип_данных

;

B. CREATE TABLE имя_таблицы

FROM имя_столбца_1 тип_данных,

имя_столбца_2 тип_данных

'. '7 '

C. CREATE TABLE имя_таблицы (

имя_столбца_1 тип_данных,

имя_столбца_2 тип_данных

3. Что из перечисленного не относится к достоинствам юлианских дат?

A. Простота вычислений с датами

B. Возможность проверки достоверности

C. Правильная сортировкач. . . . • '•' . ' . '. у : .,1 • : '

D. Ускорение процесса работы

E. Приспособленность для хранения времени

4. На какой строке выполнение этой команды будет прервано из-заошибки?

SELECT first_name | |

" " I I

•;,'.; last_name

"Full Name"

FROM plsql!01_person;

A. 1

- . B . 2 ; - ; ; ,с. зD. 4

• : E. 5' .. i . .-: . , , •• :• ;,j4^ ,••: ;• . -: j

F. Команда будет выполнена успешно

Сохранение и выборка данных: основы

5. Какие из перечисленных символов нельзя использовать в математиче-ских формулах внутри SQL-оператора?

А. +

С.]

D. *

F-/

6. Каков правильный синтаксис присваивания псевдонима столбцу?

A. SELECT имя_столбца ALIAS псевдонимFROM имя_таблицы;

B. SELECT имя_столбца псевдонимFROM имя_таблицы;

C. SELECT псевдонимFROM имя_таблицы;

D. ASSIGN псевдоним ТО имя_столбца;

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

или информацию другого типа• ' - ••"':'•• . ' • ' ' .*".

Объяснение Тип данных столбца определяет тип (а часто и длину)данных, которые будет хранить столбец.

2. С. CREATE TABLE имя_таблицы (имя_столбца_1 тип_данных,имя_столбца_2 тип_данных

Объяснение Просмотрите раздел "Создание более сложной табли-цы", и особенно код на рис. 2.6, чтобы освежить в памяти этот материал.

3. D. Ускорение процесса работы.

Объяснение Повышение скорости не является основанием для ис-пользования юлианских дат. Их применение обусловлено гораздо боль-шей функциональностью и универсальностью по сравнению с любымальтернативным вариантом, основанным на тексте.

4. В. 2

Объяснение Проблема возникнет из-за двойных кавычек, окружа-ющих пробел на строке 2. Пробел — это текст, а текст должен заключа-ться в одиночные, а не двойные кавычки. Единственным исключением

58 Глава 2

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

5.С.Е. ],{

Объяснение К математическим операторам относятся символы +, -,*,и().

6. В. SELECT имя_столбца псевдонимFROM имя_таблицы\

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

1

-

Глава

Более сложныеманипуляции с данными

60. .._.„ ' . : : ' ' . - ; . . - : ' . - ' : ' * . • . - - • . • : • • • - - • ГлаваЗ

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

Ограничение диапазонавыбираемых записей

Одной из наиболее распространенных операций, которые вы будете выпол-нять при выборе записей из таблицы, является получение определенного под-множества записей. Это подмножество будет постоянно меняться взависимости от поставленных вопросов (например: "Какие клиенты не получа-ли от меня сообщений более двух недель?" или "Какие товары продавались пар-тиями, содержащими более чем 100 штук за последние 30 дней?"). Подобнаяфильтрация записей выполняется путем добавления к оператору SELECT спе-циальной конструкции WHERE. В ней указываются условия (conditions), кото-рым должны удовлетворять отображаемые записи. Синтаксис выглядитследующим образом:

SELECT столбцы FROM имя_таблицы WHERE условие(я);

Например, можно потребовать, чтобы дата последнего контакта с клиентомотстояла от текущей более чем на две недели; или чтобы дата продажи товараприходилась на последние 30 дней, и при этом общее проданное крличествопревышало 100 штук. Давайте выполним несколько упражнений. Чтобы вамбыло с чем работать, создадим заново таблицу PLSQLIOIJPRODUCT, увели-чив число столбцов, и поместим в нее записи:

DROP. TABLE plsqliaijproduct;

CREATE' ТАВЪЕ plsqll01_product (

product_name VARCHAR2(25),product_price NUMBER(4,2),

quantity_on_hand NUMBER(5,0),last_stockdate DATE

INSERT INTO plsql!01_product VALUES

('Small Widget', 99, 1, '15-JAN-03');INSERT INTO plsql!01_product VALUES

('Medium Wodget1, 75, 1000, '15-JAN-02');

INSERT INTO plsql!01_product VALUES,,,

rtr.; . J'Cjtirpme. Phoobar'.,.50, 100,, ' 15-JAN-03') ;

INSERT INTO plsq!101_product VALUES

('Round Chrome Snaphoo', 25, 10000, null);

Более сложные манипуляции с данными 61

Фильтрация записей по числовым значениямСуществует несколько способов фильтрации записей на основе значений в

числовых столбцах. Можно выводить только записи с определенным значени-ем в столбце, или записи со значениями, большими (или меньшими) заданнойвеличины, или записи со значениями, лежащими в некотором диапазоне.

• ' ' " ' . , , : f- - - ! : ; .

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

но 1, введите следующую команду:

SELECT * FROM plsql!01_productWHERE quantity_on_hand = 1;

Результат показан на рис. 3.1. Теперь, чтобы лучше освоить эту технику, вы-берите записи, в которых цена равна 25.

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

SELECT * FROM plsql'101_ptoductWHERE quantity_on_hand < 500;

Введите эту команду и сравните ее результаты с показанными на рис. 3^2.Последняя команда исключала записи, для которых количество товара на

складе равно 500. Если вам нужно выбрать записи со значениями, меньшимиили равными определенной величине, поставьте знак равенства после знака"меньше, чем". Чтобы увидеть, к чему приводит такое изменение, введите сле-дующую пару команд:

SELECT * FROM plsql!01_productWHERE quantity_on_hand < 1000;

SELECT * FROM plsql!01_productWHERE quantity_on_hand <= 1000;

* Ur.iclB SQL-Plus

£ie Edit Search Qptions HelpSQL> SELECT « F R O M plsqll Byproduct

2 WHERE quantity_on_hand - 1;

PRODUCT НИНЕ PRODUCT_PRICE QUflNTITV_OH_HAND LflST_STOC

Snail Widget

SQL>

-iLJ

99 1 15-JAH-03

Рис. З.1. Выбор записей со значениями, совпадающимис определенным числом

62 Глава 3

• & Oracle SQL-Plus

File Edit Search Options HelpSQL> SELECT * FROM plsqll 01 product

2 WHERE quantity_on_hand < 500;

PRODUCT_NflME PBODUCT_PRICE QUflNTITV_ON HfiND LflST STOC

Snail Widget 99 1 15-JflH-83Chrome Phoobar 58 100 15-JflN-l03

SIJL>

- • ' •:•'•• : . ; . . - .

' - . : • ;. . : • - . . , ' • - • . ' ::•'.. ' •

«I I

•TTTnTxl

d

4Рис. З.2. Выбор записей со значениями, которые меньше

определенного числа

Результаты их выполнения показаны на рис. 3.3. Первая команда не выво-дит запись о товаре Medium Wodget, поскольку в этой записи количество в точ-ности равно 1000. Вторая команда, в которой указано, что значения могут бытьменьше или равны 1000, включает записьо Medium WodgetB выходные данные.

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

Ж Oracle SQL'Plus

File £dit Search Options HelpSQL> SELECT » FROM plsql181_product

2 WHERE quantitp_on_hand < 1000;

PRODUCT NAME PRODUCT_PRICE qUHNTITV_ON_HflND LflST_STOC

Small Widget 99Chrome Phoobar SO

SQL>SQL> SELECT * FROM plsqll01_product

2 WHERE quantity_on_hand <= 1000;

1 15-JflN-03100 15-JflN-03

PRODUCT NAME PRODUCT_PRICE QUftNTITY_ON_HflND LflST_STOC

Small WidgetMedium WodgetChrome Phoobar

SQL>

997550

1 15-JRN-031000 15-JRN-02100 15-JRH-03

тшт

Рис. 3.3. Выбор записей со значениями, меньшими или равнымиопределенному числу

Более сложные манипуляции с данными 63

SELECT * FROM plsql!01_product

WHERE quantity_on_hand > 1000;

SELECT * FROM plsql!01_productWHERE quantity on hand >= 1000;

' ,.' i

Выбор записей по диапазону значенийСледующий шаг состоит в выборе записей, содержащих значения из опреде-

ленного диапазона. Чтобы определить диапазон, нужно просто задать его ниж-ний и верхний пределы. Для этого вам придется освоить новую технику, аименно: научиться указывать, что для прохождения записи через фильтр дол-жны быть одновременно выполнены два разных условия. На самом деле тут нетничего сложного: достаточно соединить два условия словом "AND". Следую-щий код показывает, как это делается:

SELECT * FROM plsq,1101_productWHERE product_price >= 50

ANDproduct_price <= 100

Использование AND между двумя условиями — это классический способопределения диапазона допустимых значений. Oracle предлагает альтернатив-ный способ получения того же результата, менее традиционный, но более на-глядный: конструкцию BETWEEN. При ее использовании предыдущий кодзапишется таким образом:

SELECT * FROM plsqllOljprdduct

WHERE product_price BETWEEN 50 AND 100;

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

Исключение записейА как быть, если требуется исключить записи со значениями из определен-

ного диапазона? Никаких проблем — просто поменяйте местами знаки "боль-ше, чем" и "меньше, чем" и поставьте OR вместо AND для соединения двухусловий. Это демонстрируется следующей командой:

SELECT * FROM plsqll01_product '

WHERE product_price < 50

OR

product_price > 100'"

Для исключения диапазона значений можно использовать и конструкциюBETWEEN, предварив ее модификатором "NOT", например:

SELECT * FROM plsql!01_product

WHERE product_price NOT BETWEEN 50 AND 100;

64 Глава 3

В только что приведенных примерах было показано, как исключать записисо значениями из некоторого диапазона. Для исключения только одного значе-ния существует более простой способ. Использовав в условии операторы "<>",вы объявляете, что значения столбца должны быть меньше или больше указан-ного числа, — иными словами, не равны ему. Чтобы увидеть, как действует этотподход, введите следующую команду:

SELECT * FROM PLSQK101_PRODUCT

WHERE PRODUCT_PRICE<> 99;

Эта традиционная техника воспринимается многими различными базамиданных. Oracle предлагает и другой способ получения того же результата: испо-льзование "!=" вместо "О". Восклицательный знак перед знаком равенства из-меняет его значение с "равно" на "не равно". Вот пример:

SELECT * FROM PLSQL101_PRODUCT

WHERE PRODUCT_PRICE = 99;

SELECT * FROM PLSQL101_PRODUCT

WHERE PRODUCT_PRICE != 99;

Как было продемонстрировано в последних командах, замена "=" на "О"или "!=" для отдельных значений приводит к тому, что Oracle отображает те за-писи, которые были бы отфильтрованы при использовании "=". Вы удивитесь,узнав, как часто это бывает полезно.

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

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

SELECT * FROM product

WHERE COLOR = 'Red1

OR

COLOR = 'Green1

ORCOLOR = 'White'

/

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

SELECT * FROM product

WHERE COLOR IN, ('Red', 'Green', 'White')/

Обратите внимание, что после функции IN идет открывающая скобка, спи-сок допустимых значений, разделенных запятыми, а затем закрывающая скоб-ка. Теперь посмотрим, как применить функцию IN к построенной вамитаблице PLSQL101_PRODUCT. Введите следующую команду:

Более сложные манипуляции с данными 65

SELECT * FROM plsql!01_product

WHERE product_price IN (50, 99); . ;..:ru

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

выражениями, нетрудно применить ту же технику к текстовым столбцам. Что-бы найти записи, значения которых совпадают с определенной текстовой стро-кой, просто укажите в конструкции WHERE, что столбец должен быть равентекстовой строке (как и любой текст в SQL-командах, она заключается в оди-ночные кавычки). В качестве примера введите следующую команду:

SELECT * FROM,plsqll01_product

WHERE product_name = 'Small Widget';

В результате ваш экран должен выглядеть так, как показано на рис. 3.4.Для указания списка значений можно использовать функцию IN, как де-

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

SELECT * FROM plsql!01_product

WHERE product_name = IN ('Small Widget', 'Round Chrome Snaphoo1);

Использование шаблоновПри поиске в текстовых столбцах часто возникает потребность отыскать не-

большой фрагмент текста в любом месте столбца — например, для поиска всехзаписей с названиями товаров, содержащими слово "Chrome" (такими, как"Chrome Phoobar" или "Round Chrome Snaphoo"). Это делается с помощью шаб-лонов (wildcards), представляющих произвольную часть текста. Чтобы найти всезаписи таблицы PLSQL101_PRODUCT, в которых название товара начинаетсяс "Chrome", введите следующую команду:

SELECT * FROM plsqllOljproduct

WHERE product_name LIKE 'Chrome%';

А Oiacle SQL'Plu

file Edit Search Options Help

SQL> SELECT * FROM plsqll B1 jroduct2 WHERE product_nane - 'Small Widget 1 ;

PRODUCT НИНЕ PRODUCT_PRIGE QUflNTITV_ON_HflND LftST_STOC

Small Widget

SQL>

1 15-JflN-C

Рис. З.4. Выбор записей, значения которых совпадают с явно указаннойтекстовой строкой

66 Глава 3

Обратите внимание, что после слова "Chrome" стоит знак процента (%). Этои есть шаблон, который, будучи указан в конце текстовой строки, означает: "заданной строкой может следовать что угодно, это все равно будет рассматрива-ться как совпадение".

Как вы могли заметить, в приведенном выше примере возвращаются не всезаписи, содержащие "Chrome" в названии товара. Дело в том, что вторая записьсодержит текст не только после слова "Chrome", но и перед ним. Чтобы вклю-чить в выходные данные обе записи, поместите знаки процента до и после сло-ва "Chrome":

SELECT * FROM plsql!01_product

WHERE product_name LIKE '%Chrome%';

Результаты двух последних команд должны выглядеть так, как показано нарис. 3.5. Здесь следует подчеркнуть один важный факт: хотя в самих командахOracle регистр символов не учитывается (вы получите одинаковые результаты скомандами SELECT, select или SeLeCt), для текста, помещаемого в одиночныекавычки с целью сравнения, это не так. Поиск "Chrome" не приведет к выводузаписей, содержащих слово "chrome" или "CHROME". В главе 5 рассказано, ка-ким способом можно обойти это ограничение.

Шаблон % представляет любое количество текста — иначе говоря, однимсимволом % можно заменить сколько угодно других символов. Существуеттакже шаблон, заменяющий только один символ. Этим шаблоном являетсязнак подчеркивания (_). Его использование показано в приведенной нижекоманде. Она возвращает все записи, в которых название товара содержит бук-ву "W", за которой идет любой символ и буква "d":

Ж. Oracle SQL'Plus

File Edit Search Qptions Help

SQL> SELECT * FROM plsqH 81_product2 WHERE product_name LIKE 'ChroneV;

PRODUCT NAME PRODUCT_PRICE QUftNTITV_OH_HflND LftST_STOC

Chrome Phoobar

SQL>SJJL> SELECT * FROM plsqll 01_product

2 WHERE product_name LIKE '

50 100 15-JHN-03

PRODUCT NAME PRODUCT_PRICE QUftNTITV_ON_HflND LfiST_STOC

Chrome PhoobarRound Chrome Snaphoo

SQL>

5025

100 15-JflN-0318000

Рис. З.5. Использование шаблонов для поиска текста

Более сложные манипуляции с данными 67

SELECT * FROM plsql!01_product

WHERE product_name LIKE '%W_d%';

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

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

Например, чтобы найти в таблице PLSQL101_PRODUCTвсе записи, где да-той последнего поступления на склад является 15 января 2003 г., введите следу-ющую команду:

SELECT * FROM plsql!01_product

WHERE last_stock_date = '15-JAN-03';

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

SELECT * FROM plsql!01_productWHERE last_stock_date = 45-JAN-2003';

Если нужно найти записи, содержащие более ранние или более поздниедаты, чем указанная, используются хорошо знакомые вам знаки "больше, чем"и "меньше, чем". Это демонстрируется в следующем коде:

SELECT * FROM plsql!01_product

WHERE last stock date > '31-DEC-02';

Для поиска дат, попадающих в некоторый диапазон, можно использоватьконструкцию BETWEEN:г./

SELECT * FROM plsql!01_product

WHERE last_stock_date BETWEEN 'Ol-JAN-03' and '31-DEC-03';

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

A. Oiacle SQL-Plus

File Edit Search Options Help

SQL> SELECT * FROM plsql101_product2 WHERE last_stock_date « 'IS-Jfl

ншиз

PRODUCT NAME PRODUCT_PRICE QUflNTITY_ON_HftND LftST_STOC

Snail WidgetChrome Phoobar

SQL>

JJLJ

99SO

1 1S-JflN-B3188 15-JflN-83

Рис. 3.6. Фильтрация записей по датам

Глава 3

SELECT * FROM plsql!01_productWHERE last stock date NOT BETWEEN 'Ol-JAN-03

1 and '31-DEC-03';

Выбор записей по null-значениямВозможно, вы заметили, что хотя две последние команды содержат строго

противоположные критерии, в их суммарные результаты не входят все записибазы данных. Такая ситуация возникает, когда некоторые записи содержатnull-значения в столбцах, перечисленных в конструкции WHERE. Null-значе-ние не удовлетворяет никакому критерию, за исключением того, который спе-циально предназначен для проверки на null. Чтобы выполнить проверку наnull, следует поместить в конструкцию WHERE параметр IS NULL, какпоказа-но ниже:

SELECT * FROM plsql!01_product

WHERE last_stock_date IS NULL;

Для поиска записей, содержащихданные в определенных столбцах, исполь-зуется IS NOT NULL:

SELECT * FROM plsql!01_productWHERE last_stock_date IS NOT NULL;

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

Параметры IS NULL и IS NOT NULL применимы также в числовых и тек-стовых выражениях; фактически, они работают со столбцом любого типа.

£Je £* £e«ch Qpfor» HelpSQL> SELECT » FROM plsqll01_prodUCt

2 WHERE last_stock_date IS HULL;

PRODUCT НИНЕ PRODUCT_PRICE QUftHTITV_OH_HAHD LflST_STOC

Round Chrome Snaphoo 25

SQL>SQL> SELECT * FROM plsqll 01_prod«JCt

2 WHERE last_stoch_date IS NOT NULL;

1B800

PRODUCT НИНЕ PRODUCT_PRICE QUflHTITY_ON_HflND LAST_STOC

Small WidgetMedium WodgetChrome Phoobar

SQL>

9975SO

1 1S-JflN-03

1880 1S-JftN-02

180 15-JftN-83

Рис. З.7. Фильтрация по null-значениям

Более сложные манипуляции с данными 69

Изменение порядка записейЧаще всего записи помещаются в таблицу далеко не в том порядке, в кото-

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

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

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

команде SELECT конструкцию ORDER BY. В ней указывается один или не-сколько столбцов, по которым Oracle будет сортировать записи. Синтаксисконструкции ORDER BY следующий:

SELECT * FROM имя_таблицы О1ШЕ&БУстолбец_сортировки', , , , , „

Чтобы увидеть его в действии, введите команду:

SELECT * FROM plsql!01_product ORDER BY product jprice;

Результаты ее выполнения показаны на рис. 3.8.

за Otacle SQL'Plus

file Edit Search Qptions HelpSQL> SELECT * FROM plsqll81 product ORDER BY product_price;

НИИ

PRODUCT НИНЕ PRODUCT_PRICE QUfiNTITY_ON_HflND LftST_STOC

Round Chrome SnaphooChrome PhoobarMedium UodgetSmall Widget

25587599

10000180 15-JflH-03

1000 1S-JflN-021 1S-JBN-03

Рис. З.8. Сортировка записей по одному столбцу

70 Глава3

Поэкспериментируйте с этой конструкцией, написав команду SELECT длясортировки записей по количеству товара на складе.

Сортировка по нескольким столбцамОтсортировав таблицу PLSQL101_PRODUCT по столбцу

LAST_STOCK_DATE, вы можете столкнуться с такой проблемой: две записитаблицы содержат одинаковые даты последнего поступления на склад. КакOracle узнает, в каком порядке расположить эти записи? Для управления по-следовательностью их вывода можно указать в конструкции ORDER BYкоманды SELECT второй столбец сортировки.

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

Чтобы посмотреть, как это делается на практике, напишем команду, кото-рая сортирует записи о товарах по дате поступления на склад, а в пределах од-ной даты — по названию товара. Вот эта команда:

1 _; SELECT * FROM plsql!01_product

ORDER BY last_stock_date, product_name;

Результаты ее выполнения должны соответствовать тому, что показано нарис. 3.9. Обратите внимание: для определения двух столбцов сортировки следу-ет просто разделить их имена запятой. Точно такой же метод разделения имейстолбцов вы использовали при выборе столбцов из таблицы.

Теперь объединим два метода: выбор столбцов по имени и сортировку запи-сей по столбцу. В последнем примере записи сортировались по крайнему пра-

A Oracle SQL-Plus

File Edit Search Options HelpSQL> SELECT » FROM plsqll81_product2 ORDER BV last_stoch_date, product_narae;

вис

/PRODUCT_NflME PRODUCT_PRICE QUflHTITV_ON_HflND LflST_STOC

Medium Uodget

Chrome Phoobar

Snail Midget

Round Chrome Snaphoo

SQL>

75589925

1888 15-JftN-82188 15-JflH-83

1 15-JflN-83

Рис. З.9. Сортировка записей по двум столбцам

Более сложные манипуляции с данными 71

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

SELECT

FROMORDER BY

last_stock_date,

product_narae,

product_price,quantity_on_hand

plsql!01_productlast_stock_date,

product_name

-•

Результаты ее выполнения должны быть такими, как на рис. 3.10.Столбцы также можно сортировать по убыванию, чтобы большие значения

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

SELECT * FROM plsqllOl product ORDER BY product_price DESC;

4l Oracle SQL'Plus

file Edit Search Qptions HelpSQL> SELECT last_stock_date,

product_name,product_price,quantity_on_handplsqll01_product

HI3QI

FROM

ORDER BV last_stock_date,product_nane

LflST_STOC PRODUCTJiflME PRODUCT_PRICE QUflNTITV_ON_HflHD

15-JAN-02 Medium Uodget

15-JftN-03 Chrome Phoobar

15-JAN-03 Small Widget

Round Chrome Snaphoo

SQL>

7550

99

25

1000

1001

10000

Рис. 3.10. Указание порядка столбцов, отражающего порядоксортировки

72 ГлаваЗ

Сортировка возможна даже по тому столбцу, который не выбран. Попро-буйте ввести такую команду:

I SELECT product_name FROM plsql!01_product ORDER BY quantity_on_hand;

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

Отображение только уникальныхзначений

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

Чтобы понять, как это реализуется на практике, вам придется заново создатьтаблицу транзакций и заполнить ее записями. Затем вы увидите, как извлечь изнее уникальные значения. Чтобы создать и заполнить таблицу, введите следую-щие команды:

DROP TABLE plsql!01_purchase;

CREATE TABLE plsql!01_purchase (product_name VARCHAR2(25),

quantity NUMBER(4,2),purchase_date DATE,

salesperson VARCHAR2(3)

INSERT INTO plsqll01_purchase VALUES

('Small Widget', 1, '14-JUL-03', 'CA');

INSERT INTO plsql!01_purchase VALUES

('Medium Wodget', 75, '14-JUL-03', 'BB')

INSERT INTO plsql!01_purchase VALUES

('Chrome Phoobar', 2, 44-JUL-03', 'GA').

INSERT INTO plsql!01_purchase VALUES

('Small Widget1, 8,. ' 15-JUL-03 ' , ''GA');

;>JSERT-INTO plsqll&l_purchase VALUES ••

('Medium Wodget', 20, '15-JUL-03', 'LB'),

NSERT INTO plsqlldl_purchase VALUES

Более сложные манипуляции с данными 73

('Chrome Phoobar', 2, 46-JUL-031, 'СА');

INSERT INTO plsql!01_purchase VALUES('Round Snaphoo', 25, '16-JUL-03', 'LB');

INSERT INTO plsql!01_purchase VALUES('Chrome Phoobar', 2, '17-JUL-03

1, 'BB' );

Выбор уникальных значений похож на выбор обычного списка значений;нужно лишь указать в команде SELECT модификатор DISTINCT, как показа-но ниже:

SELECT product_name

FROM plsql!01_purchaseORDER BY product name;

SELECT DISTINCT product_name

FROM plsql!01_purchaseORDER BY product_name;

Введя обе эти команды, сравните полученные результаты с теми, что показа-ны на рис. 3.11.

Е1е |<й Sea* Cptions Help " : :: Л '^

SQL> SELECT product_name2 FROM plsqll81_purchase3 ORDER BV product_nane;

PRODUCTJfflME

Chrome PboobarChrome PhoobarChrome PhoobarMedium UodgetMedium UodgetRound SnaphooSmall WidgetSmall Widget

8 rows selected.

SQL>SQL> SELECT DISTINCT product_name

2 FROM plsqll81_purchase3 ORDER BV product_nane;

PRODUCT НЙИЕ

Chrone PhoobarMedium UodgetRound SnaphooSmall Widget

SQL>

Рис. 3.11. Выбор уникальных значений из таблицы

74 ГлаваЗ

К тем же результатам приведет использование модификатора UNIQUE вме-сто DISTINCT, как в следующей команде:

SELECT UNIQUE product_nameFROM plsql!01_purchase

ORDER BY product_name;

Модификаторы DISTINCT и UNIQUE дают одинаковый эффект, ноDISTINCT более распространен, поэтому он и будет использоваться в этой книге.

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

SELECT DISTINCT salesperson

FROM plsql!01_purchaseWHERE purchase_date BETWEEN 'Ol-JUL-03' AND 45-JUL-03

1

ORDER BY salesperson;

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

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

SELECT product_name, product_price + sales_tax

FROM plsq!101_purchase;SELECT product_name, 100 - product_priceFROM plsqll01_purchase;SELECT product_name, sales_tax / product_price

FROM plsql!01_purchase;

Можно пойти дальше и определить в операторе SELECT все значения, кото-рые должны использоваться в вычислениях, т.е. вообще не выбирать значенияиз таблицы. Для примера попробуйте ввести следующую команду, которая уве-личивает число 18 на 5 процентов:

SELECT 18*1.05 FROM plsql!01_purchase;

Экран неоспоримо свидетельствует, что результат вычислен — столько раз,сколько записей в таблице. Согласитесь — было бы удобно иметь таблицу, гдегарантированно присутствует только одна запись. Светлые головы из Oracleтоже об этом подумали и предусмотрели в процессе установки Oracle созданиетаблицы с именем DUAL, доступной всем пользователям. Чтобы увидеть, какона устроена, введите следующие команды:

DESC DUAL;SELECT * FROM DUAL;

Как видите, таблица DUAL содержит один столбец (с именем DUMMY) иодну строку (ее значение просто равно X). Данные из таблицы DUAL не рас-считаны на непосредственное использование. Эта таблица предназначена дляподдержки "моментальных" запросов, подобных только что выполненному.Попробуйте к ней обратиться, введя следующую команду:

Более сложные манипуляции с данными 75

SELECT 18*1.05 FROM DUAL;. " • . • '

В ответ вы получите только один экземпляр числа 18.9, как показано нарис. 3.12.

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

, ,

Модификация данных в таблицеИзменить данные в таблице Oracle очень легко. Используемая для этого

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

UPDATE имя_таблицы SET имя_столбца = новое_значениеWHERE условие;

A Oiacle SQL'Plufile Edit Search flptions Help

SQL> SELECT 18*1.85 FROM plsql181_purchase;

18*1.85

•гага

18.918.918.918.918.918.918.918.9

8 rows selected.

SQL> DESC DUAL;Нам Null?

DUMMV

SQL> SELECT * FROM DUAL;

D

X

SQL> SELECT 18*1.85 FROM DUAL;

18*1.85

18.9

SQL> I

Type

UftRCHflR2(1)

Рис. 3.12. Выбор значений из DUAL

76 Глава 3

Например, чтобы в таблице PLSQL101_PURCHASE переименовать все то-вары "Small Widget" в "Large Widget", введите следующую команду:

SELECT * -FROM plsq!101_purchase;

UPDATE plsqll01_purehase t ;SET product_name = 'Large Widget'WHERE product_name = 'Small Widget';

SELECT * FROM plsqllOljourchase;

Теперь выберите изтаблицы PLSQL101_PURCHASEBce записи. Ваш экрандолжен выглядеть так, как показано на рис. 3.13.

,-t Oracle SQL-Plus

£8е £сй 'Search Options Help

SQL> SELECT » FROM plsql101_purchase;

PRODUCT NAME QUflNTITY PURCHASE_ SAL

Small WidgetMedium UodgetChrome PhoobarSmall WidgetMedium WodgetChrome PhoobarRound SnaphooChrome Phoobar

8 rows selected.

1 14-JUL-03 Cfl

75 1U-JUL-83 BB

2 -U-JUL-B3 GO

8 15-JIIL-83 Gfl

20 15-JUL-03 LB

2 16-JUL-03 Си

25 16-JUL-83 LB

2 17-JUL-03 88

SQL>

SQL> UPDATE plsqliei_purchase2 SET product name - 'Large Widget

1

3 WHERE productfnane - •Small Widget';

2 rows updated.

SQL>SQL> SELECT •

PRODUCT NftHE

FROM plsqliei_purchase;

QUflNTITV PURCHASE, SAL

Large UidgetMedium WodgetChrome PhoobarLarge UidgetMedium WodgetChrome PhoobarRound SnaphooChrome Phoobar

8 rows selected.

SO,L> |

1 ni-jUL-вз та75 1U-JUL-03 BB

2 1I|-JUL-B3 GA8 15-JUL-03 GA

20 15-JUL-03 LB2 16-JUL-03 CA

25 16-JUL-03 LB2 17-JUL-03 BB

11

Рис. 3.13. Обновление данных в определенных записях

Более сложные манипуляции с данными 77

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

-

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

лицами, — это умение удалять записи. Команда DELETE имеет следующийсинтаксис:

DELETE FROM имя_таблщы WHERE условие;

Эту команду легко использовать — возможно, даже слишком легко. Хорошоподумайте, какие условия вы будете в ней указывать!

Удаление записей, соответствующих заданномукритерию

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

SELECT * FROM plsqll01_purchase;-

DELETE FROM plsql!01_purchaseWHERE purchase_date > 45-JUL-03

1;

SELECT * FROM plsqll01_purchase;

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

(или столбцы). Например, следующая команда удалит записи, где упоминается"Large Widget":

•DELETE FROM plsqllOl_purchaseWHERE product_name = 'Large Widget ' ;

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

Удаление всех строкКак последний вариант можно удалить все строки таблицы. Для этого при-

меняются два способа: использовать команду DELETE, не указывая условиеWHERE, или использовать новую для вас команду TRUNCATE (Усечь).

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

DELETE FROM имя_таблицы;

Эта команда легко воспринимается при чтении кода, но у нее есть сущест-венный недостаток: хотя она указывает на необходимость удаления всех запи-сей без исключения, Oracle все равно будет сначала считывать каждую запись,

78 Глава3

* Oiacle SQL-Plus

Fte Edit Search Qptions Це1р

SQL> SELECT * FROM plsql181_purchase;

PRODUCT NflME QUANTITV PURCHftSE_ SflL

Large Widget

Hediun Uodget

Chrome PhoobarLarge Widget

Medium UodgetChrome Phoobar

Round Snaphoo

Chrome Phoobar

8 rows selected.

1 Ht-JUL-83 Cft

75 1U-JUL-83 BB

2 m-JUL-83 Gfl8 15-JUL-ea Gfl

28 15-JUL-B3 LB

2 16-JUL-03 СЙ

25 16-JUL-83 LB

2 17-JUL-B3 BB

SQL>

SQL> DELETE FROM plsqllB1_purchase

2 WHERE purchase_date > 'IS-JUL-OS1;

3 rows deleted.

SQL>

SQL> SELECT « FROM plsql181_purchase;

PRODUCT_NftME QUANTITV PURCHftSE_ SflL

Large Widget

Medium Uodget

Chrome Phoobar

Large Midget

Mediun Uodget

SQL>

1 1U-JUL-83 Cfl

75 1H-JUL-B3 BB

2 Ht-JUL-83 Gfl8 15-JUL-B3 Gfl

28 15-JUL-83 LB

Рис. 3.14. Удаление записей по дате

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

Усечение таблицыОсновное достоинство команды TRUNCATE — скорость. Когда Oracle вы-

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

Команда TRUNCATE имеет следующий синтаксис:

TRUNCATE TABLE имя_таблицы;

Чтобы увидеть ее в действии, усеките свою таблицу PLSQL101_PURCHASE,а затем просмотрите ее содержимое, использовав следующие команды:

Более сложные манипуляции с данными 79

I ^ TRUNCATE TABLE pisql!01_purchase;

SELECT * FROM plsqll01_purchase;

ПредупреждениеКоманда TRUNCATE необратима! Используйте ее только в случаедействительной необходимости!

Управление транзакциямиДо этого момента вы создавали и удаляли таблицы, вставляли и удаляли за-

писи — короче говоря, делали все, что хотели, не задумываясь о том, как это мо-жет повлиять на других пользователей. В реальной жизни вам придетсяработать с таблицами, содержащими чужие данные, и выполненные вами из-менения неизбежно затронут других пользователей. Чтобы отвечать за своидействия, вы должны понимать, каким образом Oracle вносит изменения в базуданных. Это немедленно принесет свою выгоду: в частности, вы научитесь ис-пользовать средства отмены (undo), предоставляемые Oracle.

Отмена транзакций DMLКогда вы вставляете, обновляете или удаляете данные, Oracle не выполняет

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

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

CREATE TABLE plsql!01_purchase (

product_name VARCHAR2(25),

quantity NUMBER(4,2),

purchase_date DATE,

salesperson VARCHAR2(3)

'

Отмена в Oracle выполняется с помощью команды ROLLBACK (Откат).Производя откат одной и более транзакций, вы сообщаете Oracle, что они недолжны применяться к базе данных. Чтобы увидеть, как это делается, введитеследующие команды:

'3 INSERT INTO plsql!01_purchase VALUES

('Small Widget1, 1, 44-JUL-03', 'CA');

INSERT INTO plsql!01_purchase VALUES

('Medium Wodget', 75, 44-JUL-03', 'BB');

SELECT * FROM plsql!01_purchase;

' •

ROLLBACK;

SELECT * FROM plsql!01_purchase;

4 Зак. 725

80 Глава 3

Сравните полученные результаты с показанными на рис. 3.15. В этом упраж-нении важно то, что первый оператор SELECT возвращает записи, тогда каквторой — нет. Команда ROLLBACK удалила записи из локальной памяти, и ониуже никогда не будут записаны в таблицу базы данных для постоянного хране-ния. В итоге все выглядит так, как будто вы вообще не вводили эти две записи.

Возможности команды ROLLBACK не ограничиваются только одним уров-нем отмены. В сочетании с другой командой, SAVEPOINT, она позволяет воз-вращаться в любую из предварительно установленных точек. КомандаSAVEPOINT имеет следующий синтаксис:

SAVEPOINT имя_точки_сохранения;

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

ВНЕBe Edit Search Qptiora Help

SQL> INSERT INTO plsqll01_purchase VALUES2 ('Small Widget1, 1, 'lii-JUL-вЗ1. ' C O ' ) ;

1 row created.

SQL> INSERT INTO plsqll01_purchase VALUES2 ('Medium Wodgef . 75, '14-JUL-03*. 'BB');

1 row created.

SQL>SQL>SQL> SELECT * FROM plsqll01_purchase;

PRODUCTJMHE QUANTI TV PURCHASE. SAL

1 1U-JUL-83 CA75 14-JUL-03 BB

Snail UidgetMedium Uodget

SQL>SQL> ROLLBACK;

Rollback complete.

SQL>SQL> SELECT * FROM plsqllM_purchase;

no rows selected, ,

SQL>

Рис. 3.15. Откат изменений данных

Более сложные манипуляции с данными 81

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

INSERT INTO plsqll01_purchase VALUES

('Small Widget1, I, 44-JUL-03', 'CA');

SAVEPOINT a;

INSERT INTO pls'qll01_purchase VALUES

('Medium Wodget', 75, 44-JUL-03', 'BB');SAVEPOINT sp_2;

INSERT INTO plsql!01_purchase VALUES

('Chrome Phoobar', 2, 44-JUL-03', 'GA'); '

SAVEPOINT third;

INSERT INTO plsql!01_purchase VALUES

('Small Widget', 8, '15-JUL-03' •, 'GA');

SAVEPOINT final_sp; ^,..,,,. •:

INSERT INTO plsql!01_purchase VALUES .-.,- ,:, , _ i. ,- ,

('Medium Wodget', 20, '15-JUL-031, 'LB');

SELECT * FROM plsqllOl purchase-;';, \.

ROLLBACK TO"final_sp;

SELECT * FROM plsql!01_purchase; •...,, ..• , .

ROLLBACK TO third;

SELECT * FROM plsql!01_purchase;

ROLLBACK TO sp_2;SELECT * FROM plsql!01_purchase;

ROLLBACK TO a;

SELECT * FROM plsqllOljpurchase;

ROLLBACK;

SELECT * FROM plsql!01_purchase;

Обратите внимание, что имена точек сохранения в этом примере никак несистематизированы. Фактически каждое имя демонстрирует, как могла бывыглядеть эта система, если бы все точки сеанса назывались аналогичнымобразом. Имена точек сохранения — всего лишь метки, поэтому они могутбыть любыми. У вас есть возможность выбирать имена, по которым будетвидно, какие данные охватывает каждая точка. Для имен точек сохраненияиспользуются соглашения, похожие на соглашения для имен таблиц истолбцов: максимальная длина — 30 символов, а первым символом должнабыть буква.

Теперь, научившись отменять изменения, вы готовы к тому, чтобы делать ихпостоянными. Соответствующая команда называется COMMIT (Завершить).Поскольку она приводит к записи всех ваших изменений в таблицу базы дан-ных (что делает невозможным откат), все точки сохранения, установленные кмоменту завершения транзакции, сбрасываются. Чтобы увидеть, как это про-исходит, введите следующие команды:

INSERT, INTO,,plsqllul_puxchase VALUES

('Small Widget', 1, '14-JUL-03', 'CA');

SAVEPOINT A;

82 Глава3

INSERT INTO plsql!01_purchase VALUES('Medium Wodget', 75, 44-JUL-03', 'BB');

SAVEPOINT B;INSERT INTO plsql!01_purchase VALUES

('Chrome Phoobar', 2, '14-JUL-031, 'GA');

SAVEPOINT C;INSERT INTO plsql!01_purchase VALUES '

('Small Widget', 8, 45-JUL-03', 'GA');SAVEPOINT D;INSERT INTO plsqllOljpurchase VALUES

('Medium Wodget', 20, '15-JUL-03', 'LB');

COMMIT;

ROLLBACK TO D;

SELECT * FROM plsql!01_purchase;

Результаты должны быть такими, как на рис. 3.16. Заметьте, что командаROLLBACK вызвала ошибку: Oracle сообщает, что точка сохранения с именем"D" не существует, хотя вы ее создавали. После'завершения изменений в табли-це все точки были сброшены, поэтому Oracle больше "не помнит" о них.

Доступность данных для других пользователей

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

Вы можете в этом убедиться, если откроете второе окно SQL*Plus (указав теже самые имя пользователя и пароль, что и в первом), измените какие-нибудьданные в первом окне и просмотрите результаты этих изменений во втором.Итак, откройте второе окно SQL*Plus и введите в каждом окне следующиекоманды:

Команды для первого окна Команды для второго окнаSQL'Plus SQL*Plus

SELECT * FROM plsql101_purchase; SELECT * FROM plsq!101_purchase;

INSERT INTO plsql101 jDurchase VALUES SELECT * FROM plsql101 purchase;('Round Snaphoo', 5, '16-JUL-031, 'CA');

COMMIT; SELECT * FROM plsq!101_purchase;

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

Более сложные манипуляции с данными 83

| I I" 1И111ЩГН1 р1Ц|г;Ц|ППЩП.Пр|

ж Oiacle SQL'Plu»File Edit Search Options Help

SQL> INSERT INTO plsqll81_purchase UflLUES2 ('Snail Widget', 1, 'lij-JUL-вЗ1 , 'Cfl');

1 row created.•

SQL> SAUEPOINT Й;

Sauepoint created.

SQL> INSERT INTO plsqll01_purchase VALUES2 С Medium Uodgef , 75,

-1«i-JUL-e3

-, 'BB');

1 row created.

SQL> SAUEPOINT B;

Sauepoint created.

SQL> INSERT INTO plsql101_purchase VALUES2 ('Chrome Phoobar', 2, 'Ui-JUL-03

1, 'Gfl');

1 row created.

SQL> SAUEPOINT C;

Sauepoint created.

SQL> INSERT INTO plsqll81 purchase UALUES2 ('Small Widget'7 8, '15-JUL-03', 'EA');

1 row created.

SQL> SAUEPOINT D;

Sauepoint created.

SQL> INSERT INTO plsqll81_purchase UALUES2 (-Medium Wodgef , 28, 'IS-JUL-вЗ', ЧВ

1);

1 row created.

SQL>SQL> COMMIT;

Commit complete.

fj

SQL>SQL> ROLLBACK TO D;

ROLLBACK TO D*

ERROR at line 1:ORA-81086: sauepoint

..•D' neuer established

,

SQL>SQL> SELECT » FROM plsqll81 purchase;

PROOUCT_NAME QUANTITV PURCHASE_ SAL

Snail WidgetMedium WodgetChrome PhoobarSmall Widget

Medium Uodget

SQL> |

1 1i»-JUL-03 CA75 14-JUL-83 BB2 14-JUL-03 GA8 15-JUL-B3 GA

2» 1S-JUL-83 LB , .,

Рис. 3.16. Завершение изменений в таблице

84 Глава3

КЩШ_0_Ш1 «-M-H_u_3L£SgL> select • fro» PLSQL1 «.PURCHASE; л

PROOUCT.NAHE QUANTITV PURCHASE. SAL

SHll Widget 1 H-JUL-Ю CAHediun Wodget 75 1»-JUL-«3 IBChrone Phoobar 2 H-JUL-II3 CRSHll Widget 1 15-JUL-n CAMedium Wodget 21 15-JUL-03 LI

sgL>

sgi>SQL>sgi>sqi>sgi> • ; ;sgL>sgL>IQL>SQL>SQL) insert into PLSQL1H PURCHASE ualue« (2 'Round SnaphoC, 5, '16-JU--2M3- . 'CA');

1 ro» created.

S«L>

SQL> ..........SPL>'S»L> , . , . , . . . . .

JQL>

sgL> •sgL>ML»sgL>sgL> con.it;

Comit couple tt.

sgL>

: • ' - ' ; '" (ч * Д

: ,

••f. •. • • :••'•' .-: :• • /•- • :.ii ;fi

Eh .£« S««*" awionisgL>sgi>SQL)SQL>SQL>HPSQL>SOL>sgL>

•••••.•••.•-••.СЖЕ;'fcW>'

;: .• ,;; "

j

', • . ' •

.: ' . !

> ' ' •'•'.' :

. •: ' ' . - . ' ' .

sgi> select i Fran PLSQI1I1_P-RCHASE;

PROOUCT.NAHE

SHll Widget •Mediun WodgetCliron PhoobarSHll widgetH-diun Wodget

sgL>sgt>sgi>SQ->

шSQL> select • Fro»

PRUDUCT.NAME............. __

SHll WidgetNediu« WodgetChron* PhoobarSHll WidgetHediun Wodget

ни

\4L>

$gt>SQL>SQL> seltct • FroH

'RODUCT NAME

SHll WidgetHediuH Wodgetсигом PhoobarSHll WidgetNediu» WodgetRound Snaphoo

6 rows selected.

»«->

• I I

gUANTITV PURCHASE. SAL

1 1J.-JUL-03 CA75 1»-JUL-»3 BB2 1»-JUL-e3 CA1 15-JUL-B3 CA21 15-JUL-.3 LI

i '/ .

rLSQL1l1.PURCHASE;

QUANTITY PURCHASE. SAL— . — .. ...... —

1 H-JUL-Ю CA75 1»-JUL--3 II2 -U-JUL-n CA1 15-JUL-U CA21 15-JUL-n LI

. I, ,

.' ; :

PLSgL1H_PURCHRSE:

QUANTITY PURCHASE. SAL

1 1J.-JUL-03 CA75 K-JUL-n BB2 1»-JUL-«3 GA1 15-JUL-03 CA21 15-JUL-03 LI5 16-JUL-03 CA

.;'S" , .

"".;",> •':- ' "-.-.".

.iH

.- ;

• j

Рис. 3.17. Влияние команды COMMIT на другой сеанс базы данных

Явное и неявное завершениеВыполняя предыдущие упражнения, вы ввели несколько команд COMMIT.

Однако явный ввод команды—это лишь один из способов завершения измене-ний в базе данных. Некоторые команды Oracle выполняют завершение преды-дущих команд, не дожидаясь ваших указаний, т.е. неявно. Говоря конкретнее,любые команды DDL (например, CREATE TABLE или DROP TABLE) неявнозавершают изменение всех не сохраненных данных перед выполнением своихфункций. Выход из Oracle (или просто закрытие SQL*Plus) также приводит кавтоматическому завершению ваших изменений.

ИтогиВ этой главе было пройдено много базового материала. Сначала вы узнали,

как с помощью конструкции WHERE оператора S ELECT ограничить диапазон

Более сложные манипуляции с данными 85

записей, возвращаемых Oracle. При фильтрации можно использовать разнооб-разные операторы сравнения, включая =, !=, >, <, О, >=, <=, BETWEEN иNOT. Вы также научились создавать объединенные условия, разделяя их сло-вом AND, и условия "или/или", разделенные словом OR. Если требуется вы-брать записи, содержащие любое значение из некоторого множества, то вместомногочисленных конструкций OR можно использовать функциюШ.

При поиске текста можно применять шаблоны"_" (для представления оди-ночных символов) и"%" (для представления группы символов), а также опера-тор LIKE. Чтобы найти записи с пустыми столбцами любого типа или,наоборот, избежать их вывода, нужно указать в конструкции WHERE операторIS NULL или IS NOT NULL.

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

Бывают ситуации, когда нужно узнать, какие значения содержит столбец,но при этом желательно увидеть только по одному экземпляру каждого значе-ния. Этого можно достичь, добавив модификатор DISTINCT сразу после клю-чевого слова SELECT. Вы можете в реальном времени выполнять вычисления сданными, не содержащимися в таблице, выбирая значения из DUAL.

Когдаделодоходит до модификации данных в таблице, применяется коман-да UPDATE. В ней вы указываете таблицу, подлежащую обновлению, значениястолбцов, а затем задаете условие, которому должна удовлетворять запись дляобновления. Условие WHERE играет очень важную роль, поскольку при егоотсутствии будет обновлена каждая запись! То же самое справедливо и при уда-лении записей по команде DELETE: если опустить конструкцик) WHERE, выудалите все записи таблицы. Если ваша цель в этом и состоит, лучше использо-вать команду TRUNCATE TABLE, которая не считывает каждую запись табли-цы перед ее удалением и поэтому работает гораздо быстрее;'

В Oracle есть средства отмены в виде группы команд, относящихся к катего-рии "управление транзакциями". Наиболее важна команда ROLLBACK, отме-няющая любые команды DML (INSERT, UPDATE и DELETE), выполненныес момента последнего завершения. Явное завершение выполняется при вводекоманды COMMIT. Неявное завершение происходит при выполнении любойиз операций DDL (в частности, CREATE и DROP), а также при выходе изSQL*Plus.

Точки сохранения обеспечивают возможность многоуровневой отмены —иначе говоря, позволяют вам решать, на какое количество команд DML выпол-нить откат. Выдавая команду SAVEPOINT, вы устанавливаете именованныймаркер, к которому можно будет вернуться по команде ROLLBACK. Точки со-хранения остаются активными до завершения изменений данных; после этого

8 6 : . . . Глава3

они исчезают. Измененные данные становятся видимы другим пользователями программам, работающим с базой данных Oracle, только после завершенияизменений. (Непонимание этого факта может легко привести к недоразуме-нию. Например, кто-то вставляет, обновляет или удаляет данные, а потомудивляется, почему изменения не видны на компьютерах его коллег. Обычноэто означает, что пользователь забыл завершить свои изменения.)

Следующая глава посвящена управлению программой SQL*Plus. Описан-ные в ней приемы помогут вам быстрее справляться с работой, получать болеепривлекательные результаты и автоматизировать выполняемые действия, что-бы их можно было повторять, не набирая заново весь код.

.

Вопросы1. Каково правильное определение термина "точка сохранения"?

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

B. Место, в котором Oracle должен приостановить обработку, пока выне дадите команду на продолжение.

C. Место среди набора команд DML, куда Oracle должен вернуться, ан-нулировав все последующие изменения.

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

2. Какое из перечисленных условий является правильным?

A. WHERE'Smith1

. ••• ' ' • . : «B. WHERE 'Job_Descriptiori = 'Manager'

C. WHERE SaLaRy - SYSDATE

D. WHERE LASTJSTAME BETWEEN'K' AND 9

E. WHERE HIREDATE BETWEEN '02-JAN-02' AND 'Ol-JAN-011

F. WHERE PRICE LIKE 10%

3. Какие из перечисленных серий команд управления транзакцией лише-ны смысла?

A. Вставка некоторых записей, COMMIT, ROLLBACK

B. ROLLBACK, вставка некоторых записей, COMMIT

C. SAVEPOINT, вставка некоторых записей, ROLLBACK,ROLLBACK, COMMIT

D. ROLLBACK, вставка некоторых записей, COMMIT, COMMIT

4. Какие символы используются в качестве шаблонов для одиночногосимвола и группы символов соответственно?

Более сложные манипуляции с данными 87

"у , ;;"; • . • ' • • • / . . . ; - ' ' - : - • • "

А.?,*

В. _, %

с.?,_D. ?,%

E. %,~ • ', • t : ,' • • • . ' , - • : , ' • ; , ' -

F.M

О.*,?

н. *, %5. На какой строке будет вьщано сообщение об ошибке при выполнении

команды с таким синтаксисом?

UPDATE имя_таблицыWHERE имя_столбца =условиеSET имя_столбца — новое _значениеORDER BY имя_столбца

A. 1

B. 2

C. 3

D. 4

E. Команда будет выполнена успешно

Ответы на вопросы1 . С. Место среди команд DML, куда Oracle должен вернуться, аннули-

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

Объяснение Команда SAVEPOINT отмечает место в серии командDML, к которому впоследствии можно будет вернуться по командеROLLBACK.

2. Е. WHERE HIREDATE BETWEEN '02-JAN-02' AND 'Ol-JAN-ОГ

Объяснение В варианте А слово "Smith" ни с чем не сравнивается.В варианте В отсутствуют кавычки вокруг имени столбца. В варианте Сстолбец, содержащий числовые значения (зарплата), сравнивается с те-кущей датой, что не имеет смысла (тот факт, что "salary" набрано нео-бычным образом, не играет роли, поскольку в именах столбцов регистрне учитывается). В варианте D сделана попытка одновременно исполь-зовать строку и число в качестве границ диапазона, заданного операто-ром BETWEEN, что не имеет смысла, поскольку диапазон долженопределяться двумя значениями одного типа. В варианте F операторLIKE использован совместно с числовым столбцом и числовым значе-нием, тогда как этот оператор работает только с текстом.

Глава3

3. А, с, о.Вставка некоторых записей, COMMIT, ROLLBACK SAVEPOINT,вставка некоторых записей, ROLLBACK, ROLLBACK, COMMITROLLBACK, вставка некоторых записей, COMMIT, COMMIT

Объяснение В варианте А нет смысла выполнять ROLLBACK послеCOMMIT, поскольку откат после завершения уже невозможен. В вари-анте С нет смысла выдавать вторую команду ROLLBACK. В варианте Dнет смысла повторять завершение.

4. В. _, %

Объяснение Обратитесь к разделу "Использование шаблонов", что-бы освежить в памяти этот материал.

5. в. ,V*; i-чОбъяснение В команде UPDATE конструкция SET должна бытьуказана до конструкции WHERE.

'

; - :

!.-Я I м$ &£$?

Глава

Управление SQL'Plus

90 Глава 4

D этой главе описывается ряд ценных приемов, позволяющих наиболее полноиспользовать возможности программы SQL*Plus. Вы узнаете, как сокращатьвремя ввода и количество нажатий на клавиши путем модификации и повтор-ного использования старых команд; как приводить SQL*Plus в "первозданныйвид", очищая экран; как настраивать среду SQL*Plus и сохранять эти настрой-ки, чтобы в следующий раз они применялись автоматически; как улучшать чи-таемость данных, полученных с помощью команд SELECT; как записыватьвыбранные данные в дисковый файл; и, наконец, как сохранять команды вфайлах сценариев, чтобы впоследствии вызывать их нажатием всего несколь-ких клавиш. Делайте меньше, и сделаете больше! Думаю, каждый согласится,что ради этого стоит потратить время на изучение материала.

Редактирование предыдущих командДо сих пор вам приходилось вводить каждую SQL-команду вручную,

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

Использование текстового редактораДаже если вы начали писать SQL-команды, только приступив к чтению этой

книги, держу пари, что вам уже приходилось набирать SQL-команду из неско-льких строк, запускать ее на выполнение и тотчас обнаруживать небольшуюошибку. Согласитесь, что неплохо иметь возможность редактирования коман-ды (подобно тому как вы редактируете фразы в текстовом процессоре)- и ее ав-томатического запуска на повторное .выполнение. Такая возможностьсуществует. После ввода в следующем приглашении SQL> команды EDIT (илиее сокращенного варианта, ED) SQL* Plus откроет текстовый редактор, исполь-зуемый на вашем компьютере по умолчанию, и автоматически загрузит в негопоследнюю SQL-команду. Вы сможете отредактировать команду, "сохранить"ее в SQL*Plus, а затем выполнить отредактированный вариант.

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

DROP TABLE plsqlldl_product;

CREATE TABLE plsqll,01_product '.{.

product_name VARCHAR2 (-25) ,

product_price NUMBER(4,2),

quantity_on_hand NUMBER(5,0),

last_stock_date DATE

Управление SQL* Plus 91

INSERT INTO plsql!01_product VALUES

('Small Widget', 99, 1, '15-JAN-03');

INSERT INTO plsql!01_product VALUES

('Medium Wodget1, 75, 1000, 45-JAN-02 ' ) ;

INSERT INTO plsql!01_product VALUES

('Chrome Phoobar', 50, 100, '15-JAN-03');

INSERT INTO plsql!01_product VALUES

('Round Chrome Snaphoo1, 25, 10000, null);'

;,-•::,•

DROP TABLE plsqllOljpurchase;

CREATE TABLE pisql!01_purchase (

product_name VARCHAR2(25),

salesperson VARCHAR2(3),

purchase_date DATE,

quantity NUMBER(4,2)

INSERT INTO plsql!0.1_purchase VALUES

('Small Widget', 'CA', '14-JUL-03', 1);

INSERT INTO plsql!01_purchase VALUES

('Medium Wodget', 'BB1, '14-JUL-03', 75);

INSERT INTO plsql!01_purchase VALUES

('Chrome Phoobar', 'GA', 44-JUL-03', 2);

INSERT INTO plsqll01_purchase VALUES

('Small Widget', 'GA', '15-JUL-03', 8);

INSERT INTO plsql!01_purchase VALUES

('Medium Wodget1, 'LB

1, 45-JUL-03', 20);

INSERT INTO plsql!01_purchase VALUES

('Round Snaphoo', 'CA1, 46-JUL-03

1, 5);

Использование команды EDITЧтобы увидеть, как работает команда EDIT, выполните следующие действия:

1. Введите показанную ниже команду. Обратите внимание, что она содер-жит несколько опечаток.

SELECT product_nmaeFROM Plsqll01_prodUctWHERE quantity_on_hand >= 100

ANDlast_stock_date IS NOT NULL

ORDER BY product_name;

2. В ответ SQL*Plus должен вывести сообщение "ORA-00942: table or viewdoes not exist". Этим Oracle хочет сказать, что имя таблицы, из которойвыбираются данные, записано неправильно. Вы должны повторитькоманду, но набирать шесть строк ради исправления двух опечаток —это слишком, поэтому...

3. Наберите edit и нажмите клавишу ENTER. Вы должны увидеть окно ре-дактора, похожее на то, что показано на рис. 4.1. (Какая программа вдействительности откроется для редактирования текста — зависит от

92 Глава4

Я afiedt.bul - Notepad

ffc Ed» Swtch HelpЯИО!

SELECT product_nnaeFROM plsq!101_produtcWHERE quantity_on_hand >- 101

ANDlast_stock_date IS NOT NULL

ORDER BV product name

Рис. 4.1. Использование команды EDIT для редактирования SQL-команд

конкретного компьютера. Например, в Windows-системах по умолча-нию используется Notepad, тогда как в Unix-системах — обычно EDили VI.) Ваша команда будет помещена во временный файл с именемнаподобие afiedit.buf. Имя не имеет значения, поскольку вам не требует-ся сохранять'команду на диске.

4. Исправьте ймя4столбца в строке 1, а также имя таблицы в строке 2.'

5. Выйдите из текстового редактора (как правило, это делается по командеFile | Exit). При этом вы получите запрос на сохранение изменений.Обычно такое приглашение означает, что будет создан (или обновлен)файл на диске, но на самом деле программа запишет отредактирован-ную команду обратно в SQL*Plus. Ответьте Yes на вопрос о сохраненииизменений.

6. Отредактированная команда автоматически запишется в SQL*Plus.Чтобы ее выполнить, введите один прямой слэш (/) и нажмите клавишуENTER.

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

Построчное редактированиеХотя возможность править команды в полноэкранном редакторе очень при-

влекательна, иногда требуемое изменение столь незначительно, что быстреезаново набрать всю команду, чем открывать текстовый редактор, вносить изме-нение и сохранять ещ В подобных случаях, вр можете воспользоваться другимсредством SQL*Plus, позволяющим редактировать предыдущую команду пря-мо в окне SQL*Plus. Этот подход не предлагает всех возможностей текстовогоредактора и поэтому не очень хорошо подходит для многострочныхSQL-команд, но выполнять изменения в коротких командах быстрее всегоименно так.

Использование команды CHANGEЛучший способ разобраться в данном подходе — это попробоватьего приме-

нить, а потом прочитать объяснение. Выполните следующие действия:

Управление SQL*Plus 93

1. Введите показанную ниже команду. Обратите внимание, что в именистолбца сделана ошибка — наберите его точно так же.

SELECT product_nmae FROM plsql!0l_product; *'•'-

2. Обратите внимание, что в выведенном сообщении об ошибке имястолбца PRODUCT_NMAE отмечено звездочкой (*). Конечно, это по-тому, что оно написано неправильно.

3. Наберите следующую команду:

change/nmae/name

Нажмите клавишу ENTER, чтобы выполнить команду CHANGE. Каквидите, SQL*Plus повторно отображает команду, на этот раз с правиль-но написанным именем столбца.

4. Для выполнения измененной команды введите слэш (/) и нажмитеENTER.

5. Сравните полученные результаты с теми, что показаны на рис. 4.2.

* Oiocle SQL-Plus

Эе Ed* Search "Options М Ф " .;.:.'". ' :SQL> SELECT product nnae FROM plsql! Byproduct;SELECT product_nnae FROM plsql181_product

*ERROR at line 1:ORA-0B9QJ»: Invalid colunn name

SQL> change/nnae/name1* SELECT product_nane FROM plsql1B1 product

SQL> /

PRODUCT NflME

ВВЕЗ

Snail WidgetHediun WodgetChrome PhoobarRound Chrome Snaphoo

SQL> |

Рис. 4.2. Исправление ошибки с помощью команды CHANGE

Как показывает этот пример, команда CHANGE позволяет заменять в ранеевведенной команде одну текстовую строку на другую. CHANGE — это некоманда SQL; она работает только в программе SQL*Plus.

В приведенном выше упражнении вы начинали команду со слова"CHANGE", Его можно сократить до "С". Длинный и короткий вариантыкоманды полностью эквивалентны; сокращение "С" просто удобнее.

94 Глава 4

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

C[HANGE] разделительный_символ старый_текст\разделительный_символ новый_текст]

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

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

Выбор строки при построчном редактированииВ двух последних примерах вы пользовались командой EDIT для модифика-

ции группы строк и командой CHANGE для модификации одиночных строк.С помощью команды CHANGE можно выполнять и многострочное редакти-рование, но это делается не так, как при использовании EDIT. Вместо того,чтобы предоставить удобную среду редактирования, где можно перемещатькурсор от строки к строке нажатием клавиш со стрелками или щелчкамимыши, команда CHANGE позволяет редактировать только по одной строке заодин раз. Чтобы исправить многострочный оператор с помощью командыCHANGE, вы должны перед внесением фактических изменений указать, с ка-кой строкой хотите работать. Для этого достаточно ввести номер строки передвызовом CHANGE. Ввод номера сообщает SQL* Plus, что данную строку нужносделать текущей.

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

SEbECT product_nmae

FROM plsql!01_produtc

WHERE quantity_on_hand >= 100AND

last_stock_date IS NOT NULLORDER BY product_name;

г

c/ma/am2

c/tc/ct/

Сравните свои результаты с показанными на рис. 4.3. Как видите, при каж-дом вводе числа SQL*Plus делает строку из предыдущей команды с этим номе-ром текущей и позволяет ее модифицировать при помощи команды CHANGE-.

Управление SQL* Plus 95

Ж Oracle SQL'Plus

File Edit Seaich Qptions HelpSQL> SELECT pi-oduct_nmae

2 FROM plsql181_pi-odutc3 WHERE quantity_on hand >- 188t AND5 last_stock_date IS NOT NULL

6 ORDER BV product_nane;

FROM plsqliei_produtc*

ERROR at line 2:ОНй-в091|2: table or uieu does not exist

SQL>

SQL> 1

1* SELECT

SQL> c/na/an

1» SELECT

SQL> 2

2» FROM

SQL> c/tc/ct

2* FROM

SQL> /

PRODUCT NfltlE

product_nmae

product_name

plsql1B1_produtc

plsqll01_product

Chrome PhoobarMedium Uodget

SQL>

Рис. 4.З. Использование команды CHANGE для многострочногоредактирования

Копирование и вставкаДовольно часто требуется повторять SQL-команду, выполненную две, три и

даже большее число команд назад. В таких случаях команды CHANGE и EDITне помогают, поскольку нужная команда больше не находится в буфере командSQL*Plus. Однако есть другой способ воспользоваться ранее введенными стро-ками. Вы можете копировать команды с экрана SQL*Plus и повторно использо-вать их, вставляя (paste) в строку приглашения SQL>. Чтобы увидеть, как этоделается, начните со ввода следующих команд:

SELECT * FROM plsql!01_product;

UPDATE plsql!01_productSET product_name = 'Large Widget

1

WHERE product_name = 'Small Widget';

Для проверки результатов выполнения команды UPDATE нет необходимо-сти заново набирать оператор SELECT. Достаточно скопировать его. Помести-те курсор мыши прямо перед буквой "S" в слове "SELECT". Нажмите левуюкнопку и проведите курсором вдоль всей команды, как если бы вы хотели ско-

Глава 4

пировать ее в текстовом процессоре. Когда вся команда будет выделена, отпус-тите кнопку мыши и откройте меню Edit в окне SQL*Plus. Выберите командуСору, чтобы поместить дубликат команды в буфер обмена Windows. Затем вы-берите пункт меню Edit | Paste, чтобы вставить команду обратно в окноSQL*Plus. Нажмите ENTER для выполнения команды. Теперь ваш экран дол-жен выглядеть так, как показано на рис. 4.4.

Для вызова команд Сору и Paste можно использовать стандартные сочета-ния клавиш (shortcuts) вашей операционной системы. Например, если вы запу-скаете SQL*Plus в среде Windows, то можете, удерживая нажатой клавишуCTRL, нажать клавишу с буквой "С" для копирования команды, а затем испо-льзовать сочетание CTRL-V для ее вставки.

Существует еще более быстрый способ. Мы применим его для копированияпоследней команды UPDATE, чтобы вернуть товару исходное название. Вы-полните следующие действия:

1. Поместите курсор мыши прямо перед буквой "U" слова "UPDATE" впервой строке команды.

2. Нажмите и удерживайте левую кнопку мыши.

3. Проведите курсором мыши вдоль всей первой строки исходной командыUPDATE.

.* Oiacle SQL-Plus ИИЕЗ

File Edit Search Options HelpSQL> SELECT * FROM plsqll01_product;

PRODUCT_NAME PRODUCT_PRICE QUAHTITV_OH_HAHO LAST_STOC

Snail Uidget 99 1 15-JAN-03

Medium Uodget 75 1000 15-JAN-02

Chrome Phoobar 50 100 15-JAN-03

Round phrone Snaphoo 25 10000

SQL>

SQL> UPDATE pisqnoi_product2 SET product name = 'Large Uidget'

3 WHERE producO>ane - 'Snail Uidget';

1 row updated.

SQL> SELECT * FROM plsqll Byproduct;

PRODUCT_HAME PRODUCT_PRICE QUANTITV_ON_HAND LAST_STOC

Large Uidget 99 1 i5-JflN-B3Medium Uodget 75 1000 15-JAN-02Chrome Phoobar 50 100 15-JAN-03Round Chrome Snaphoo 25 10000

SQL> |

Рис. 4.4. Результаты копирования и вставки SQL-команды

Управление SQL*Plus 97

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

5. Нажмите клавишу ENTER для перехода на новую строку в приглашенииSQL> (ее приглашением будет "2").

6. Поместите курсор мыши перед буквой "S" слова "SET" во второй строкеисходной команды UPDATE.

. ' . - '.7. Нажмите и удерживайте левую кнопку мыши.

8. Проведите курсором мыши по второй строке, остановившись передбуквой "L" слова "Large". Вам нужно включить в выделение одиночнуюкавычку перед "Large", но не "L".

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

10. Наберите слово Small и поставьте после него пробел.

11. Выделите оставшуюся часть второй строки исходной команды UPDATEи скопируйте ее точно так же, как вы это делали уже два раза.

12. Нажмите клавишу ENTER для перехода на новую строку в приглашенииSQL>.

13. Выполните аналогичную обработку третьей строки исходной командыUPDATE, заменив "Small" на "Large".

14. Нажмите ENTER для запуска команды.

Описанный способ годится и для команд, которые в ходе прокрутки исчезлиза верхней границей окна SQL*Plus. Вы можете прокрутить содержимое окнадо появления нужной команды, применить этот способ, и команда будет встав-лена в текущую строку приглашения SQL>.

Очистка экрана SQL*PlusВыполняя описанные в этой книге упражнения, вы уже ввели десятки, а воз-

можно, и сотни SQL-команд. Не возникало ли у вас желание очистить экранSQL*Plus — вернуть его к первоначальному виду, чтобы ликвидировать беспо-рядок? Это довольно легко сделать. Нажмите клавишу SHIFT, а потом, не отпу-ская ее, клавишу DELETE. Вы увидите диалоговое окно, показанное нарис. 4.5. Щелкните на кнопке ОК., и на экране SQL*Plus останется только при-глашение SQL>.

Настройка среды SQL*PlusМногие параметры SQL*Plus можно менять. Изменение большинства этих

параметров не дает большого выигрыша, но некоторые из них очень полезны.Сначала мы посмотрим, как изменять их через меню SQL*Plus, а затем — какиспользовать для этого командную строку SQL*Plus.

98 Глава 4

Oracle SQL-Plus

Are you sure you want to clear the Screen and the Screen Buffet?

O'K" Cancel

Рис. 4.5. Диалоговое окно для очистки экрана SQL*Plus

Настройка с использованием меню SQL*PlusНаходясь в SQL*Plus, выберите пункт меню Options | Environment. Это при-

ведет к появлению диалогового окна Environment (Среда), показанного нарис. 4.6. В левой половине этого окна находится прокручиваемый список оп-ций, а правая половина содержит две установки, управляющие объемом буфераSQL*Plus. По умолчанию SQL*Plus запоминает до 100 символов каждой вве-денной строки и до 1000 таких строк. Однако в окне Environment вы можетеувеличить длину хранимой строки до 1000 символов (временами это полезно), ачислострок—до 2000 (это применяется довольно часто, поскольку строки дан-ных, выводимые в ответ на ваши команды, тоже учитываются). Изменив этизначения, вы сможете максимизировать количество предыдущих команд и ихрезультатов, хранимых SQL*Plus, в результате чего вам будет проще возвращать-ся назад и находить команды для копирования или результаты для сравнения.

Чтобы изменить ширину и длину буфера, введите 1000 в поле Buffer Width и2000 в поле Buffer Length.

Что касается левой части диалогового окна Environment, то сейчас нам при-годятся две опции из списка Set Options: linesize (размер строки) и pagesize (раз-мер страницы). Linesize задает максимальную ширину строки, при которойSQL*Plus еще не будет выполнять перенос. Когда эта величина слишком мала,

Environment

Set Options

arraysize 'Uautocommitautoprintautotraceblockterminator

cmdsepcolsepcompatibilityсо neatcopycommitcopytypecheck j

jraiuu

С Delault

current

Г On

f Off

11

Buffer Width: J100

Buffer Length: J1000

1 . . . .

Cancel | OK

j

, i1

I

Рис. 4.6. Диалоговое окно настройки среды SQL*PluS

Управление SQL* Plus 99

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

Величина linesize не зависит от параметра Buffer Width, измененного ранее.Linesize определяет, какую ширину могут иметь строки, тогда как Buffer Widthопределяет, сколько символов каждой строки будет храниться в памяти для по-следующего извлечения. Есть смысл сделать оба значения одинаковыми, что-бы все данные, которые вы видите, сохранялись в буфере.

Чтобы установить значение linesize равным Buffer Width, прокрутите списокSet Options вниз, пока не увидите опцию linesize. Выделите ее, а затем щелкнитена переключателе Current в панели Value. Поле ввода в нижней части панелиValue станет активным. Введите в это поле значение 1000. Теперь щелкните накнопке ОК, чтобы закрыть диалоговое окно Environment. Это изменение будет

£ Oiacle SQL-Plus

File Edit Search Options Help

SQL> select * from PLSQL101_PRODUCT;

PRODUCT НИНЕ PRODUCT_PRICE QUaNTITV_ON_HflND

LflST_STOC

Small Widget15-JflN-03

Medium Uodget

15-JflN-02

Chrome Phoobar15-JflN-83

PRODUCT_HftME

LflST_STOC

Round Chrome Snaphoo

99 1

' ' , ' • :75 1000

- . ' " I .

50 100

PBODUCT_PRICE QUflNTITY_ON_HAND

25

SQL>

>Гл

Рис. 4.7. Результат переноса данных на другую строку

100 Глава4

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

Другая полезная опция, pagesize, определяет, сколько строк данных будетотображаться по команде SELECT до того, как SQL*Plus начнет повторять за-головки столбцов. Значение по умолчанию довольно мало, и при использова-нии современных дисплеев с высоким разрешением это приводит котображению множества наборов заголовков на одном экране. Предпочтите-льней устанавливать для pagesize значение 9999, чтобы лишь самые длинныесписки данных содержали более одного набора заголовков. Можете попробо-вать эту установку, введя 9999 в поле значения опции pagesize.

Закончив изменение значений в диалоговом окне Environment, закройтеего, щелкнув на кнопке ОК.

Настройка с использованием командВсе опции, показанные в списке Set Options диалогового окна Environment,

можно изменить из командной строки SQL*Plus. В качестве примера введитепосле приглашения SQL> следующие команды:

SET LINESIZE 1000

SET PAGESIZE 9999

Сохранение настроек средыSQL*Plus позволяет сохранять все настройки своей среды в файле, который

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

1. Определите путь к базовому каталогу Oracle (Oracle home) на жесткомдиске вашего компьютера. В базовом каталоге хранятся файлы

* Oracle SQL'Plus • • > ' • • • - - НЕС

File Edit Se«ch Qpttons Help

SQL> select « from PLSQL101_PRODuCT; .d

PRODUCT_NAME PRODUCT_PRICE QUANTITV_ON_HAND LflST_STOC

Small Widget 99 1 15-JAN-03Medium Uodget 75 1000 15-JAN-02Chrome Phoobar 50 100 15-JAN-03Round Chrome Snaphoo 25 10000

SQL>

-iLJ

Рис. 4.8. Результат установки более подходящего значения linesize

Управление SQL*Plus 101

программ Oracle. Если вы работаете в Windows NT, откройте программуWindows Explorer (Start | Programs | Windows NT Explorer) и поищите ка-талог с именем, похожим на "OraNT". Если вы запускаете SQL*Plus вWindows 95,98, 2000 или более поздней версии Windows, то следует ис-кать каталог, имя которого похоже на "ORAWIN95". В крайнем случаеобратитесь к администратору базы данных.

2. Теперь введите в SQL*Plus следующую команду:

STORE_SET диск:огас1е_Лол!е\ОВ8\ЬОС1Н.ЗОЬ APPEND

Подставьте вместо диск:огас1е_Ноте букву дискового накопителя и имябазового каталога Oracle на вашем компьютере. Обратите внимание, чтоэту команду не нужно завершать точкой с запятой, поскольку онауправляет только программой SQL*Plus и ничего не посылает базе дан-ных Oracle.

Команда STORE указывает SQL*Plus на необходимость сохранения всех те-кущих настроек среды в дисковом файле, Этот файл, login.sql, расположенныйв подкаталоге DBS базового каталога Oracle, автоматически считывается призапуске SQL*Plus. Сохраняя свои настройки в этом файле, вы гарантируете ихвосстановление при каждом следующем входе в SQL*Plus.

Форматирование выходных данныхSQL*Plus

Вероятно, вы уже заметили, что SQL*Plus очень мало заботится о красотеотображаемых записей. Он не выравнивает количество десятичных знаков вчислах, обрезает заголовки столбцов по своему усмотрению и настойчиво по-казывает все содержимое большого текстового столбца в одной строке, незави-симо от того, насколько широким будет этот столбец на экране. Всего лишьнесколько простых команд могут радикально улучшить внешний вид выход-ных данных SQL*Plus.

Секрет заключен в команде COLUMN. Как и STORE, она не взаимодейст-вует с базой данных Oracle. Ее единственная задача — повлиять на способ ото-бражения информации вашей копией SQL*Plus. По этой причине командыCOLU MN не требуется завершать точкой с запятой, как стандартные командыSQL. Кроме того, они действуют только во время сеанса работы с SQL*Plus.Если выйти из SQL*Plus, в следующий раз придется вводить все эти командызаново.

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

INSERT INTO plsqll01_product VALUES (

'Extra Huge Mega Phoobar +',

9.95,1234,

102 Глава4

Форматирование чиселОбычно с числами требуется осуществлять три действия:

• Выравнивать количество десятичных знаков

• Помещать разделитель между сотнями, тысячами и т.д.

• Добавлять знак денежной единицы

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

Выравнивание количества десятичных знаковКоманда COLUMN, выравнивающая количество десятичных знаков, имеет

следующий синтаксис:

COLUMN имя_столбца FORMAT код_формата

Вместо аргумента имя_столбца подставляется имя того столбца, который выхотите отформатировать. Обратите внимание: здесь не упоминается, какойтаблице принадлежит столбец! Команда COLUMN воздействует на все столб-цы с указанным именем, независимо от того, в какой таблице они находятся.Впрочем, если два столбца имеют одинаковые имена, они с большой вероятно-стью содержат похожие данные, и форматирование, примененное к одному изних, обычно имеет смысл и для другого.

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

Чтобы увидеть, как работает команда COLUMN, введите следующий код:

SELECT * FROM plsqll01_product;

COLUMN product_price FORMAT 9999.99

SELECT * FROM plsqll01_product;

Сейчас ваш экран должен выглядеть примерно так, как показано на рис. 4.9.Как видите, поначалу значения в столбце PRODUCT_PRICE не были выров-нены, а после выдачи команды COLUMN выровнялись.

Добавление разделителя групп разрядовРазделитель групп разрядов (group separator) — это символ, разделяющий сот-

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

COLUMN quantity_on_hand FORMAT 99,999

•SELECT * FROM plsql!01_product;

Управление SQL*Plus 103

• * Oiacle SQL'Plus

^£ite Edit Search Options Help !SQL> SELECT « FROM plsq!101_product ;

PRODUCT_HAME

Small WidgetMedium UodgetChrome PhoobarRound Chrome Snaphoo

SQL>SQL> COLUMN product priceSQL>

PRODUCT_PRICE QUANTITV_ON_HflND LAST_STOC

99 1 15-JflN-0375 1008 15-JAH-0250 100 15-JAH-0325 10000

FORMAT 9999.99

BflLoOL J

d. ,

'

SQL> SELECT * FROM plsqll Byproduct ;

PRODUCT_NAME

Small UidgetMedium WodgetChrome PhoobarRound Chrome Snaphoo

SQL>

iJ

PRODUCT_PRICE QUANTITV_ON_HflND LAST_STOC

99. BB 1 15-JAN-8375.08 1000 15-JAN-0250.08 188 15-JAN-0325. OB 18888

. . : '. .. .- : '

-

•, .

&"••-~J M

Рис. 4.9. Использование команды COLUMN для выравниванияколичества десятичных знаков

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

Добавление знака денежной единицыЛегко догадаться, что речь идет об очередной разновидности кода формата.

Попробуйте ввести следующий код, чтобы поместить знак доллара ($) передкаждым значением PRODUCT_PRICE:

COLUMN product_price FORMAT $99.99

SELECT * FROM plsqllOljproduct;- • . ,

Другие полезные коды форматовТаблица 4.1 содержит список наиболее важных кодов форматов, которые

можно использовать с числами. Поэкспериментируйте с каждым из этих ко-дов, чтобы лучше понять их назначение. Обратите внимание на код RN — этовас немного развлечет.

Форматирование текстаОдним из наиболее часто отмечаемых недостатков SQL*Plus является то,

что он не переносит со строки на строку содержимое больших текстовыхстолбцов. Действительно, из-за этого работа с программой становится менееудобной. Однако есть простой способ заставить SQL*Plus переносить текст встолбцах. Для этого используется разновидность команды COLUMN со сле-дующим синтаксисом:

104 Глава 4

Таблица 4.1. Коды числовых форматов

Элемент

$

,(запятая)

. (точка)

Ml

PR

D

RN или rn

О

О

Пример

$9999

9,999

99.99

9999MI

ЬУУУУ

9999PR

99D99

9G999

С999

L999

RN

0999

Описание ;

Помещает знак доллара перед значением

Помещает запятую в указанной позиции

Помещает десятичную точку в указанной позиции

Отображает знак минуса (-) после любого отрицательно-го числа

Помещает в указанной позиции знак плюса (+) для поло-жительных чисел и знак минуса (-) для отрицательных

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

Отображает в указанной позиции десятичный раздели-тель, принятый в вашей стране

Отображает в указанной позиции разделитель групп раз-рядов, принятый в вашей стране

Отображает в указанной позиции знак денежнойединицы ISO

Отображает в указанной позиции знак местной денежнойединицы

Отображает числа римскими цифрами верхнего или ниж-него регистра (только для целых чисел от 1 до 3999)

Отображает один и более начальных нулей

Отображает пустые значения в виде нулей

COLUMN имя_столбца FORMAT Ann WORD_WRAP

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

Введите следующие команды:

SELECT * FROM plsqllOljproduct;

COLUMN product_name FORMAT AID WORD_WRAP

SELECT * FROM plsqll01_product;

Управление SQL*Plus 105

Как видите, названия товаров теперь легко помещаются в узком простран-стве. Вы можете подумать: "Они помещались там и раньше". Это верно. Данныйспособ лучше применять для более широких столбцов, содержащих по 30, 40,50,100 или даже несколько сот символов. Так почему я не предложил упражне-ние, демонстрирующее работу с таким длинным текстом?

А хотите ли вы набирать названия товаров из сотни букв? Думаю, что нет.Просто помните об этом способе, чтобы быть готовым к ситуациям, когда ши-рокий столбец не помещается на экране SQL*Plus.

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

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

COLUMN имя_столбца HEADING 'текст_заголовка'JUSTIFY LEFT

или

COLUMN имя_столбца HEADING 'текст_заголовка'JUSTIFY CENTER*

или

COLUMN имя_столбца HEADING 'текст_заголовка 'JUSTIFY RIGHT

Кроме управления содержимым заголовков, вы получаете возможность ис-пользовать смесь символов верхнего и нижнего регистров и разбивать заголов-ки на несколько строк. Вертикальная черта ( | ) в аргументе текст_заголовкаобозначает перевод строки. Вы даже можете указать, как должен быть выров-нен заголовок: по левому краю, по правому краю, или по центру.

Это может быть проиллюстрировано примером:

SELECT * FROM plsqll01_product;

COLUMN product_name HEADING 'Product I Name' JUSTIFY CENTER

SELECT * FROM plsqll01_product;

Вы можете собрать вместе все опции команды COLUMN, чтобы использо-вать их одновременно. Попробуйте ввести перечисленные ниже команды, а по-том сравните результаты с теми, что показаны на рис. 4.10.

SELECT * FROM plsqllOljproduct;

COLUMN product_name FORMAT A10 WORD_WRAP HEADING 'Name'

JUSTIFY CENTER

COLUMN product_price FORMAT $99.99 HEADING 'Price' JUSTIFY RIGHT . ,.

COLUMN quantity_on_hand FORMAT 99,999 HEADING 'On|Hand'

JUSTIFY RIGHT

COLUMN last_stock_date HEADING 'Last|Stock|Date' JUSTIFY RIGHT

SELECT * FROM plsql!01_product;

106 Глава 4

Ж Oiacle SQL-Plus

File Edit Seach Options Help

SQL> SELECT * FROM plsq!1B1_prodUCt;

ProductName PRODUCTJ>RICE QUANTITV_ON_HAND LAST_STOC

SnailWidget

MediunWodget

Chrome

$99.00

$75. ее

$58.80

1 15-JAN-B3

1,888 1S-JflN-82

188 15-JAN-B3Phoobar

Round $25.80 1(1,880

ChromeSnaphoo

SQL>SQL> COLUMN product nane FORMAT A10 WORD_URAP HEADING 'Name' JUSTIFV CENTER

SQL> COLUMN product_price FORMAT $99.99 HEADING 'Price1 JUSTIFV RIGHT

SQL> COLUMN quantity_on_hand FORMAT 99,999 HEADING 'On|Hand' JUSTIFV RIGHT

SQL> COLUMN last_stock_date HEADING 'Last(Stock(Date' JUSTIFV RIGHT

SQL>

SQL> SELECT » FROM plsq!181_product;

Last

On StockNane Price Hand Date

SmallWidget

MediunUodget

ChromePhoobar

RoundChromeSnaphoo

SQL>

JJJ

$99.BB 1 15-JAN-B3

$75.88 1,888 1S-JAN-02

$58.88 188 15-JAN-B3

$25.BO 18,088

Рис. 4.10. Форматирование текста с использованием команд COLUMN

Для отключения форматирования, заданного командой COLUMN, исполь-зуется следующий синтаксис:

COLUMN имя_столбца OFF

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

Управление SQL'Plus 107

COLUMN product_name OFF

COLUMN product_price OFF

COLUMN quantity_on_hand OFF

COLUMN last_stock_date OFF

SELECT * FROM plsqllOl product;

Буферизация выходных данныхна диске

Буферизация (spooling) — это процесс записи информации в дисковый файл.Иногда это удобно делать прямо из SQL*Plus, чтобы сохранить серию команд срезультатами их выполнения или объемные выходные данные одной команды.(Если выходные данные умещаются на одном экране SQL*Plus, можно простовыделить мышью нужный фрагмент, скопировать его в буфер обмена, а затемвставить в любую нужную программу. Возможно, после вставки потребуетсяустановить для этих данных моноширинный шрифт, чтобы сохранить вырав-нивание строк.)

Команда SPOOL имеет следующий синтаксис:

SPOOL имя_буферного_файла

При желании можете включить в имя буферного файла расширение (напри-мер, .sql или .ргп). Если расширение не указано, имя автоматически дополняет-ся расширением .1st. Кроме того, имя буферного файла может содержать путь(path), т.е. имя дискового накопителя и каталога, где должен быть сохраненфайл. Если путь не указан, буферный файл сохраняется в подкаталоге BIN ба-зового каталога Oracle.

Чтобы увидеть, как выполняется буферизация, введите перечисленныениже команды. Обратите внимание, что после команд SPOOL не нужно ста-вить точку с запятой, поскольку они являются внутренними командамиSQL*Plus и не влияют на работу сервера Oracle.

SPOOL c:\plsqll01_test.prn

SELECT * FROM plsql!01_product;

SELECT * FROM plsql!01_purchase;SPOOL OFF

\

ПримечаниеЕсли вы запускаете SQL *Plus в Unix, путь к буферному файлу будетиметь примерно такую структуру:

/uO 1/user/plsql 101_test. prn

(Не забывайте, что в путях и именах файлов Unix учитываетсярегистр символов.)

После выполнения этих команд запустите Windows Explorer или File Mana-ger и перейдите в каталог, где вы сохранили файл plsqll01_test.prn. Откройтеего, и вы увидите все, что выводилось на экран SQL*Plus на протяжении ваше-го сеанса.

108 Глава 4

Файлы сценариев SQLК этому моменту вы изучили порядочное количество различных

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

Создание файла сценарияФайл сценария (script file) представляет собой обычный текстовый файл. Вы

можете создать его в любом текстовом редакторе или процессоре. (При исполь-зовании текстового процессора не забудьте сохранить файл командой Save As вформате "text only", чтобы он не содержал кодов форматирования.) Вы даже мо-жете воспользоваться командой EDIT программы SQL*Plus, чтобы запуститьстандартный текстовый редактор операционной системы и создать в нем но-вый файл сценария. Чтобы увидеть, как это делается, выполните следующее:

1. Введите в SQL*Plus такую команду:

EDIT c:\plsqll01_test.sql

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

2. Находясь в текстовом редакторе, введите в файл plsq!101_test.sql следу-ющие команды:

CREATE TABLE plsql!01_temp (first_name V A R C H A R 2 ( 1 5 ) ,last_name VARCHAR2(25)

INSERT INTO plsqll01_temp VALUES ( ' Joe ' , 'Smith ' ) ;INSERT INTO plsq!101_temp VALUES ( ' Jane ' , 'Mi l le r ' ) ;

SELECT •* FROM plsq!101_temp;

DROP TABLE plsql!01_temp;

3. Выйдите из текстового редактора. На вопрос о сохранении только чтосозданного файла ответьте Yes.

Управление SQL*Plus 109

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

Запуск сценарияЗапустить сценарий в SQL*Plus очень легко. Нужно лишь поставить перед

именем файла знак "at" (@). Введите в строке приглашения SQL> следующуюкоманду:

@с:\plsqll01_test

Экран с результатами ее выполнения показан на рис. 4.11. Обратите внима*ние, что в команду не потребовалось включать расширение .sql. Если расшире-ние не указано, предполагается, что файл имеет расширение .sql.

Использование переменных в файлах сценариевВременами возникает потребность в сценарии, который мог бы выполнять

разные действия в зависимости от ситуации. Этого можно достичь за счет испо-льзования переменных (variables). Переменная заменяет часть команды, позво-ляя вводить эту часть при выполнении сценария и тем самым влиять на его

-Г Oiacle SQL-Plus

File £dit Search Options Help

SQL> EDIT c:\plsql101_test.sql

SQL> Bc:\plsqliei_test

Table created.

1 row created.

1 row created.

mwss

FIRST NAME LAST NAME

JoeJane

Table dropped.

SQL>

SmithMiller

Рис. 4.11. Запуск сценария SQL

110 Глава 4

работу. (Противоположностью переменным является информация, явно ука-занная в файле сценария. Информация такого типа называется жестко закоди-рованной (hard-coded), поскольку ее нельзя изменять во время выполнениясценария.) Переменные можно встраивать в сценарии SQL двумя способами:использовать переменные подстановки или команду ACCEPT.

Переменные подстановкиИспользование переменной подстановки (substitution variable) — это самый

простой способ встроить переменную в сценарий. Чтобы увидеть, как это дела-ется, создайте файл сценария с именем plsql 101_test2.sql и поместите в него сле-дующие команды:

SET VERIFY OFF

SELECT product_name, quantity, purchase_date

FROM plsql!01_purchaseWHERE quantity >= &minimum_quantity_sold

SET VERIFY ON

Сохраните и запустите этот сценарий. Вы увидите приглашение на ввод зна-чения minimum_quantity_sold ("Enter value for mmimum_quantity_sold"). Вве-денное число будет помещено в конструкцию WHERE. Введите значение 20 ипосмотрите, как работает сценарий. Потом запустите его снова (для этого до-статочно ввести слэш (/) и нажать ENTER) и в ответ на приглашение введитезначение 5, чтобы увидеть, как меняется поведение сценария.

Команды SET VERIFY OFF и SETVERIFY ON помогают улучшить воспри-ятие сценария. Без них SQL*Plus будет показывать старое и новое значение пе-ременной подстановки перед выполнением команды SELECT, что приведет взамешательство любого другого пользователя, запускающего сценарий, а вконце концов начнет раздражать и самого создателя сценария.

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

SET VERIFY OFF

SELECT product_name, quantity, purchase_date

FROM plsql!01_purchase

WHERE purchase_date = '&date_you_want_to_select'

SET VERIFY ON

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

Управление SQL* Plus 111

Поместите этот код в файл сценария с именем plsqll01_test3.sql и запуститеего. (Сейчас таблица содержит записи с датами 14-JUL-03, 15-JUL-03 и16-JUL-03.)

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

Команда ACCEPTКак вы могли заметить, приглашение, которое SQL*Plus выдает пользовате-

лям, встречая переменную подстановки, выглядит не слишком привлекатель-но. Альтернативой является команда ACCEPT, которая позволяет определятьприглашение произвольного вида. Эта команда имеет следующий синтаксис:

accept имя_переменнойprompt 'текстприглашения'

Чтобы увидеть, как она работает, создайте сценарий с именемplsq!101_test4.sql и поместите в него следующие команды:

SET VERIFY OFF

SET ECHO OFF

ACCEPT v_earliest_date

PROMPT 'Earliest date you would like to see? (dd-mmm-yy): '

ACCEPT v_latest_date

PROMPT 'Thank you. Latest date you would like .to see?(dd-mmm-yy): '

SELECT product_name, quantity, purchase_date

FROM plsql!01_purchase

WHERE purchase_date BETWEEN '&v_earliest_date' AND '•&v_latest_date'ORDER BY product_name, quantity

SET VERIFY ON

SET ECHO ON

В этом сценарии задействована пара новых команд: SET ECHO OFF и SETECHO ON. Команды SET ECHO включают и выключают отображение командсценария на экране. В данном случае они гарантируют, что пользователь уви-дит только приглашения из команд ACCEPT, но не сами эти команды.

ИтогиV

В этой главе был описан ряд приемов, позволяющих наиболее полно испо-льзовать возможности программы SQL*Plus. Вначале вы познакомились с тем,как редактировать и повторно использовать команды. Это делается либо с по-мощью команды ED, вызывающей стандартный текстовый редактор вашей си-стемы, либо с помощью команды CHANGE, обеспечивающей построчноередактирование. Если команды, которые вы намерены использовать повторно,не требуют изменения, можно просто скопировать их с экрана SQL*Plus и сно-ва вставить в текущей позиции курсора.

5 Зак. 725

112 Глава 4

Научившись повторять предыдущие команды, вы узнали, как очищать эк-ран SQL*Plus, используя сочетание клавиш SHIFT-DELETE. Затем вы научи-лись настраивать различные параметры среды SQL*Plus, как через пункт менюOptions | Environment, так и с помощью команд, вводимых непосредственно вприглашении SQL*Plus. Чтобы эти изменения действовали в последующих се-ансах, их можно сохранять с помощью команды STORE.

Облегчить чтение выбранных данных помогает команда COLUMN, позво-ляющая форматировать числа, даты и текст, а также определять и выравниватьзаголовки столбцов. Чтобы сохранить в дисковом файле все, что выводилось наэкран SQL*Plus, можно воспользоваться командой SPOOL.

Одним из самых мощных способов повышения эффективности работы сSQL является хранение часто используемых групп команд в текстовых файлах срасширением .sql, называемых файлами сценариев. Для запуска этих сценари-ев достаточно ввести в приглашении SQL*Plus символ @, указав после него имяфайла, содержащего нужные команды. Чтобы сделать сценарии более универ-сальными, в них можно включать переменные подстановки и командыACCEPT, позволяющие вводить критерии (или любую другую информацию) входе выполнения сценария.

Теперь двинемся дальше. Нас ждет действительно трудный материал!Не волнуйтесь, я вас обманул. На самом деле следующая глава совсем не

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

Вопросы1. Какой редактор запускается при вводе команды ED в SQL*Plus?

A. Внутренний редактор Oracle

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

C. Программа EDIT

D. Программа VI

2. Какая из перечисленных команд не изменяет способ отображения дан-ных на экране?

A. COLUMN

B. SET LINESIZE

C. SPOOL

D. SET PAGESIZE

3. На какой строке будет выдано сообщение об ошибке при выполненииэтой команды?

SELECT product_name, quantity, purchase_dateFROM plsql!01_purchaseWHERE quantity <= &maximum_quantity_sold;

A. 1' - '. • •'• ' '.' :• ' :.-/:'• H7 '

Управление SQL*Plus 113

в. 2C. 3D. Команда будет выполнена успешно

4. На какой строке будет выдано сообщение об ошибке при выполненииэтой команды?

SET VERIFY OFF ••".;•SET ECHO OFF

ACCEPT v_earliest_date PROMPT 'Earliest date? (dd-mmm-yy):. '. -ACCEPT v_latest_date PROMPT 'Latest date? (dd-mmm-yy): ' ,.

SELECT product_name, quantity, purchase_dateFROM plsql!01_purchase

WHERE purchase_date BETWEEN '&earliest_date' AND •'Sla-'test_date'

ORDER BY product_name, quantity;SET VERIFY ONSET ECHO ON

A. 1

B. 3

C. 5' ' , . - : -' S • - - - . . J

D. 7

E. 9

Ответы на вопросы1. В. Редактор, используемый в вашей системе по умолчанию

Объяснение Oracle не имеет собственного редактора. Команда EDзапустит ту программу редактирования, которая используется в вашейсистеме по умолчанию.

2. С. SPOOL

Объяснение Команда SPOOL просто записывает отображаемые наэкране данные в дисковый файл. Она никак не влияет на них.

3. О. Команда будет выполнена успешно

Объяснение Единственное, что необычно в данной командеSELECT, — это использование символа & в третьей строке. Наличие та-кого символа означает, что следующее за ним имя (maximum_quantity_sold) относится к переменной подстановки. Это приведет к выдаче при-глашения на ввод соответствующего значения при выполнении коман-ды. Синтаксис абсолютно правильный.

4. D. 7

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

Часть

SQL дляпрофессионалов

- .

Глава

Встроенные функции SQL

118 _ Глава 5

В этой части книги описываются разнообразные средства SQL, применениекоторых позволяет сделать приложения Oracle более простыми в использова-нии, а также повысить их мощность и эффективность. В главе 6 вы будете изу-чать индексы, ограничения и связи, а в главе 7 познакомитесь с рядом мощныхсредств SQL, которые помогут вам выполнять повседневные задачи по управ-лению базой данных наиболее оптимальным способом.

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

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

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

DROP TABLE plsql!01_product;CREATE TABLE plsql!01_product (

product_name VARCHAR2 ( 2 5 ) ,

product_price NUMBER (4, 2),

quantity_on_hand NUMBER (5, 0) ,laststockdate DATE

INSERT INTO plsql!01_product VALUES

('Small Widget', 99, 1, ' 15-JAN-03 ' ) ;

INSERT INTO plsql!01_product VALUES

('Medium Wodget', 75, 1000, ' 15-JAN-02 ' ) ;

INSERT INTO plsql!01_product VALUES

('Chrome Phoobar ' , 50, 100, ' 15-JAN-03 ' ) ;INSERT INTO plsql!01_product VALUES

('Round Chrome Snaphoo', 25, 10000, null);.INSERT INTO plsq!101_product VALUES

('Extra Huge Mega Phoobar +', 9.95, 1234, ' 15-JAN-04 ' ) ;

DROP TABLE plsql!01_purchase;

CREATE TABLE plsql!01_purchase (

product_name VARCHAR2 (25) ,

salesperson VARCHAR2(3),

Встроенные функции SQL 119

purchase_date DATE,quantity NUMBER(4,2)

INSERT INTO plsql!01_purchase VALUES('Small Widget

1, 'CA', '14-JUL-03', 1);

INSERT INTO plsql!01_purchase VALUES

('Medium Wodget', 'BB', '14-JUL-03', 75);

INSERT INTO plsql!01_purchase VALUES

('Chrome Phoobar', 'GA', 44-JUL-03', 2);

INSERT INTO plsql!01_purchase VALUES

('Small Widget1, 'GA', '15-JUL-03', 8);

INSERT INTO plsq!101_purchase VALUES

('Medium Wodget', 'LB', '15-JUL-03', 20);

INSERT INTO plsql!01_purchase VALUES

('Round Snaphoo', 'CA', '16-JUL-03', 5);

Часто используемыеоднострочные функции

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

• Системные переменные

• Числовые функции.

• Символьные (текстовые) функции

• Функции для работы с датами

• Функции преобразования данных

• Прочие функции

Системные переменныеСистемные переменные создаются Oracle и содержат информацию о среде, в

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

SYSDATEФункция SYSDATE возвращает текущие дату и время. Если говорить точ-

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

SELECT SYSDATE FROM DUAL;

120 Глава 5

В ответ на экране будет показана текущая дата. Однако интереснее исполь-зовать эту команду в операторах DML. Например, указав SYSDATE в операто-ре INSERT, можно вставить текущую дату в любое поле данных, Попробуйтеввести такую команду:

INSERT INTO plsql!01_purchase VALUES':

('Small Widget1, 10, sysdate, 'SH');

Теперь просмотрите все записи таблицы PLSQL101_PURCHASE с помо-щью оператора SELECT, и вы увидите, что в таблицу была добавлена новая за-пись с текущей датой в столбце PURCHASE_DATE. (На самом деле значение вэтом столбце содержитне только дату, но и время^ Скоро вы узнаете, как вывес-ти временной компонент.)

Усложним этот пример, проведя некоторые вычисления с датами в операто-ре INSERT. Введите следующие команды:

INSERT INTO plsql'101_purchase VALUES

('Medium Wodget', 15, sysdate-14, 'SH');

INSERT INTO plsql!01_purchase VALUES

('Round Snaphoo', 25, sysdate-7, 'SH');

INSERT INTO plsq!101_purchase VALUES('Chrome Phoobar', 10, sysdate+7, 'SH');

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

Функция SYSDATE полезна также при просмотре записей с датами, кото-рые определенным образом связаны с сегодняшним днем. Например, чтобыувидеть все продажи за последние 30 дней, можно воспользоваться следующейкомандой:

SELECT * FROM plsql!01_purchase

WHERE purchase_date .BETWEEN (SYSDATE-30) AND SYSDATE;

Результаты ее выполнения показаны на рис. 5.1.Те'перь поэкспериментируйте с SYSDATE самостоятельно. Например, по-

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

DELETE FROM plsql!01_purchaseWHERE SALESPERSON = 'SH

1;

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

не смогу привести реальный пример ее использования. То же самое относитсяи к следующей функции, USERENV. Обе эти функции полезны, когда вамнужно вести аудит действий над таблицей, т.е. следить за тем, кто вставляет, об-новляет и удаляет записи. Это относительно сложная задача; о том, как она ре-шается, вы узнаете из главы 9. Тем не менее, чтобы свести все функции в однуглаву, я описываю их здесь. Когда вы снова встретитесь с ними, они уже будутвам знакомы.

Встроенные функции SQL 121

A Oiacle SQL'Plus

£Je £d» Search Qptions HelpSQL> SELECT « FROM plsqllB1_purchase

2 UHERE purchase_date BETWEEN (SVSDATE-30) ftND SVSDfiTE;

ЯМЕ

PRODUCT НЙМЕ QUflNTITV PURCHASE. SflL

Snail UidgetMedium UodgetRound Snaphoo

SQL>

10 05-JUL-BO SH

15 21-JUN-B8 SH

25 28-JUN-Oe SH

Рис. 5.1. Выбор записей при использовании SYSDATE в конструкции WHERE

Функция USER возвращает идентификатор пользователя Oracle, которыйвыдал команду, содержащую эту функцию. Чтобы понять смысл сказанного,попробуйте ввести следующую команду:

SELECT USER FROM DUAL;

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

USERENVФункция USERENV может возвращать множество разных сведений о вы-

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

SELECT USERENV('TERMINAL') FROM DUAL;

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

Числовые функцииЧисловые функции оперируют с числовыми значениями, изменяя их в соот-

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

122 Глава 5

ROUNDФункция ROUND округляет числа с любой заданной точностью. Она имеет

следующий синтаксис:

ROUND(exodHoe_3Ha4emie, число_знаков_после_десятичной_точки)

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

Рассмотрим пример. Таблица PLSQL101JPRODUCTсодержит цены на то-вары. Некоторые (но не все) из этих цен являются целыми числами без дробнойчасти. Чтобы увидеть, как работает функция ROUND, введите следующиекоманды:

SELECT product_name, product_price

FROM plsql!01_product;

SELECT product_name, ROUND(product_price, 0)

FROM plsql!01_product;

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

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

* Oracle SQL-Plus

File Edit Seaich Options HelpSQL> SELECT product_nane, product_price

2 FROM plsql1B1_product;

PRODUCT NflHE PRODUCT PRICE

Small Widget 99

Medium Wodget 75

Chrome Phoobar 50

Round Chrome Snaphoo 25

Extra Huge Mega Phoobar » 9.95

SQL>

SQL> SELECT product•name, ROUND(product_price, 0)

2 FROM plsqll Byproduct;

PRODUCT_HflME ROUND(PRODUCT_PRICE,8)

Small Widget 99

Medium Wodget 75

Chrome Phoobar se

Round Chrome Snaphoo 25

Extra Huge Mega Phoobar + 10

SQL> |

Рис. 5.2. Функция ROUND

Встроенные функции SQL 123

/зывать любое число десятичных знаков, но некоторые значения имеют большесмысла, чем другие. Если указать отрицательное число, то функция ROUNDначнет округление до десятичной точки, иначе говоря, числа будут округлятьсядо ближайших десятков, сотен, тысяч и т. д.

Лучший способ увидеть связь между точностью, задаваемой в функцииROUND, и изменением, которое производится в обрабатываемом числе, — этоприменить функцию к числам, содержащим много десятичных знаков. Поско-льку ваши тестовые таблицы не содержат записей с такими числами, да и нетникакого смысла создавать подобные записи, мы воспользуемся таблицейDUAL. Введите перечисленные ниже команды, чтобы посмотреть, как отража-ется количество десятичных знаков, указанных в функции ROUND, на спосо-бе округления переданного ей числа. Результаты сведены в таблицу 5.1.

SELECT ROUND(1234.5678, 4) FROM DUAL;SELECT ROUND(1234.5678, 3) FROM DUAL;SELECT ROUND(1234.5678, 2) FROM DUAL;SELECT ROUND(1234.5678, 1} FROM DUAL;SELECT ROUND(1234.5678, 0) FROM DUAL;SELECT ROUND(1234.5678, -1) FROM DUAL;SELECT ROUND(1234.5678, -2) FROM DUAL;SELECT ROUND(1234.5678, -3) FROM DUAL;

Таблица 5.1. Результаты применения функции ROUND

Функция ROUND Возвращаемое значение

ROUND(1234.5678,4) 1234.5678

ROUND( 1234.5678, 3) 1234.568

ROUND( 1234.5678, 2) 1234.57

ROUND( 1234.5678,1) 1234.6

ROUND( 1234.5678,0) 1235

ROUND(1234.5678, -1) 1230

ROUNDf 1234.5678,-2) 1200

ROUNDf 1234.5678,-3} 1000

TRUNCФункция TRUNC усекает число, понижая его точность. Различие между

усечением и округлением проявляется, когда за последним из остающихся де-сятичных разрядов идет значение 5 и выше. Округление привело бы к увеличе-нию содержимого последнего разряда на 1, тогда как при усечении этого непроисходит. Введите следующую серию команд, чтобы понять, о чем идет речь.Результаты сведены в таблицу 5.2.

124 Глава 5

SELECTSELECTSELECTSELECTSELECTSELECTSELECT

TRUNC(1234TRUNC (1234TRUNC(1234TRUNC(1234TRUNC(1234TRUNC(l-234TRUNC(1234

.5678,

.5678,

.5678,

.5678,

.5678,

.5678,

.5678,

4)

3)

2)

1)

0)

-1)-2)

FROMFROMFROMFROMFROMFROMFROM

DUAL;DUAL;DUAL;DUAL;DUAL;DUAL;DUAL;

SELECT TRUNC(1234.5678, -3) FROM DUAL;

Таблица 5.2. Результаты применения функции TRUNC

Функция TRUNC Возвращаемое значение

TRUNC( 1234.5678,4) 1234.5678

TRUNC( 1234.5678,3) 1234.567

TRUNC( 1234.5678, 2) 1234.56

TRUNC( 1234.5678,1) 1234.5

TRUNC(1234.5678,0) 1234

TRUNC(1234.5678,-1) 1230

TRUNC( 1234.5678, -2) 1200

TRUNC( 1234.5678, -3) 1000

Текстовые функцииТекстовые функции, называемые в Oracle символьными функциями (character

functions), оперируют с текстовыми строками. Чаще всего с текстовыми стро-ками требуется делать следующее: изменять регистр символов (на верхний,нижний или смешанный); разбивать длинные строки на несколько более ко-ротких подстрок; очищать текст, поступающий из внешнего источника, от из-быточных пробелов в конце. Изучив описанные ниже текстовые функции, вынаучитесь выполнять все эти операции.

UPPER, LOWER и INITCAPЭти три функции меняют регистр переданного им текста. Поскольку их на-

значение очевидно, сразу приведу примеры:

SELECT UPPER(product_name) FROM plsqll01_product;SELECT LOWER(product_name) FROM plsq!101_product;SELECT INITCAP(productjname) FROM plsqll01_product;

Результаты выполнения этих команд показаны на рис. 5.3. Как можно заме-тить, функция INITCAP в данном случае ничего не делает, поскольку каждоеслово в названиях товаров и так начинается с большой буквы. Способностьэтой функции наводить порядок в тексте гораздо лучше демонстрирует следую-щая команда:

Встроенные функции SQL 125

A Oiacle SQL'Plus

£ite £А iearch Qptions fclelpSQL> SELECT UPPER(product_nane) FROM plsq!101_product;

UPPER(PHODUCTJtt ME)

ВНЕ

SMflLL UIDGET

MEDIUM UODGETCHROME PHOOBflR

ROUND CHROME SNAPHOO

EXTRA HUGE MEGA PHOOBAR +

SQL> SELECT LOWER(product_nane) FROM plsql101_product;

LOWER(PRODUCT_NAHE)

snail midgetmedium wodgetchrome phoobarround chrome snaphooextra huge mega phoobar +

SQL> SELECT IHITCAP(product_nane) FROM plsql101_product;

INITCAP(PRODUCT_NAME)

Small WidgetMedium WodgetChrome PhoobarRound Chrome SnaphooExtra Huge Mega Phoobar +

SQL>

Рис. 5.3. Результаты применения функций UPPER, LOWER и INITCAP

SELECT INITCAP('this TEXT hAd UNpredictABLE caSE1) FROM DUAL;

:

Из всех трех символьных функций, меняющих регистр, чаще всего исполь-зуется функция UPPER. Она очень полезна в операторах SELECT, когда нетуверенности, какими буквами был набран текст в столбце. В таких случаях до-статочно подставить имя столбца в функцию UPPER, а искомый текст набратьв верхнем регистре. Чтобы увидеть это на конкретном примере, вам придетсянемного изменить тестовые записи: в них должно встречаться одно и то же сло-во, но с разным регистром символов. Приведенный ниже код вносит это изме-нение, демонстрирует использование функции UPPER для преодоленияразличий в регистрах, а затем возвращает данные к исходному виду. Введитекоманды и сравните результаты с показанными на рис. 5.4.

UPDATE plsqll01_productSET product_name = 'chrome phoobar'WHERE product_name = 'Chrome Phoobar';SELECT * FROM plsql!01_productWHERE UPPER(product_name) LIKE '%PHOOBAR%';

126 Глава 5

У, Oiacle SQL-Plus

File Edit Search Options Help

SQL> SELECT * FROM plsqllB1_prodUCt2 WHERE UPPER(product_name) LIKE '

PRODUCT NflHE PRODUCT_PRICE QUflNTITV_OH_HflND LflST_STOC

chrome phoobarExtra Huge Mega Phoobar

SQL>

509.95

188 15-Jf)N-031234 15-JflN-84

Рис. 5.4. Использование функции UPPER для упрощения поиска текста

Используя функцию UPPER таким образом, можно значительно облегчитьпоиск текста, когда неизвестно, в каком регистре он был введен. Однако, какпоказано на рис. 5.4, выходные данные по-прежнему остаются несогласован-ными в части использования символов верхнего и нижнего регистров. Чтобыпривести значения столбца PRODUCTJStAMEK единому виду, нужно приме-нить к нему функцию INITCAP. Введите следующий код:

SELECT INITCAP(product_name),

product_pr,ic,e,quantity_on_hand,last_stock_date

FROM plsql!01_product

WHERE UPPER(product_name) LIKE '%PHOOBAR%';

Сравнив результаты с показанными на рис. 5.5, верните названия товаров ктому виду, который они имели до этого упражнения:

UPDATE plsqll01_product

SET product_name = 'Chrome Phoobar'

WHERE product_name = 'chrome phoobar';

LENGTHВременами требуется определять длину данных, хранимых в столбце табли-

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

SELECT product_name, LENGTH(product_name) NAME_LENGTHFROM plsql!01_product

WHERE LENGTH(product_name) > 15ORDER BY product_name;

Встроенные функции SQL 127

Ж Oracle SQL'PlusFile Edit Seach Options Help

SQL> SELECT INITCAP(product_name),2 product price,3 quantity_on_hand,<t last_stock_date5 FROM plsql101_product6 WHERE UPPER(product_nane) LIKE 'tPHOOBftRV;

ИГЛ S3

INITCRP(PRDDUCT_NflME) PRODUCT_PRICE QUBNTITV_uN_HflND LflST_STOC

Chrome PhoobarExtra Huge Mega Phoobar

SQL>

509.95

ЛОв 1S-JflN-031234 15-JfiN-e4

Рис. 5.5. Использование функции INITCAP

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

Функция LENGTH полезна также для определения максимальной и сред-ней длины текста в столбце. О том, как это делается, вы узнаете чуть ниже вэтом разделе.

SUBSTRРаботая с Oracle, вы рано или поздно столкнетесь с такими ситуациями, ког-

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

ITEMJD

LA-101LA-102LA-ЮЗLA-104NY-101NY-102NY-ЮЗNY-104

в несколько столбцов, например, таким образом:

MANUFACTURERJ.OCATION

LALALA

MANUFACTURERJTEM.NUMBER

101102103

128 Глава 5

LA 104NY 101NY 102NY 103NY 104

В компьютерном мире текст называется строкой (string), поэтому фрагменттекста называется подстрокой (substring). В только что приведенном примерекаждая строка из столбца ITEMJD была разбита на две подстроки. Этот про-цесс называется разбором (parsing) строки.

Для выполнения данной операции Oracle предоставляет функцию SUBSTR.Чтобы разрезать строку на подстроки, необходимо указать, в какой позициидолжна начинаться подстрока и какой длины она должна быть. В предыдущемпримере первая подстрока содержала местонахождение производителя. Онаначиналась с первой позиции столбца ITEM_ID и имела длину в два символа.Следующая подстрока содержала номер изделия, начиналась с четвертой пози-ции и имела длину в три символа.

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

CREATE TABLE plsqll01_old_ltem (

item_id CHAR(20),

item_desc CHAR(25)

INSERT INTO plsq!101_old_item VALUES

('LA-101', 'Can, Small');INSERT INTO plsqll01_old_item VALUES

('LA-102', 'Can, Large');

INSERT INTO plsq!101_old_item VALUES

('LA-ЮЗ', 'Bottle, Small1);

INSERT INTO plsqll01_old_item VALUES

('LA-104', 'Bottle, Large');

INSERT INTO plsq!101_old_item VALUES

('NY-101', 'Box, Small');

INSERT INTO plsq!101_old_item VALUES

('NY-102', 'Box, Large');

INSERT INTO plsq!101_old_item VALUES

('NY-ЮЗ', 'Shipping Carton, Small');

INSERT INTO plsq!101_old_item VALUES

('NY-104', 'Shipping Carton, Large');

Теперь, когда у вас есть данные для разбора, можно привести синтаксискоманды SUBSTR. Вот он:

$\ЗВ8ТЩисходный_текст, позиция_начального_символа,количество^символов)

Встроенные функции SQL 129

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

SELECT SUBSTR(item_id, 1, 2) MFGR_LOCATION,

SUBSTR(item_id, 4, 3) ITEM_NUMBER,

item_desc

FROM plsq!101_old_item

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

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

INSTRФункция SUBSTR, использование которой демонстрировалось в послед-

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

£ Oiacle SQL-Plus НИ ЕЗfile Edit S ch Qptjons це|р

SQL> SELECT SUBSTR(itemjld. 1. 2) MFGR_LOCflTIOH,2 SUBSTR(item_id, 4, 3) ITEH_NUHBER,3 iten_desc4 FROM plsql101_old item5 ;

MF ITE ITEM DESC

Lfl 1B1 Can, SnailLA 102 Can, LargeLfl 183 Bottle, SmallLfl 104 Bottle, LargeNY 101 Box, SnailNV 162 Box, LargeNY 103 Shipping Carton, SnailNV 18U shipping Carton. Large

8 rows selected.

SQL> |

Jl

Рис. 5.6. Разбор данных с использованием функции SUBSTR

130 Глава 5

Единственный способ разобрать такую строку состоит в том, чтобы найти по-зицию символа (или символов), разделяющего элементы строки . Именно это иделает функция INSTR.

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

Синтаксис функции INSTR выглядит следующим образом:

INST~R(ucxodHbtu_meKcm, текст _для_поиска, позиция _началъного_символа)

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

Функция INSTR может помочь нам в разделении столбца ITEM_DESC таб-лицы PLSQL101_OLD_ITEMHa две части. Все, что нужно сделать, — это найтиположение запятой, отделяющей категорию изделия от размера изделия. Ис-пользуя возвращенное функцией число, мы можем указать правильный размердля категории изделия, а также начальную позицию для его размера.

Коль скоро мы собираемся прибегнуть к помощи функции INSTR для раз-деления столбца ITEM_DESC таблицы PLSQL101_OLD_ITEM, нужно пони-мать, какую информацию выдает эта функция для каждой записи. Введитеследующую команду:

SELECT item_desc,INSTR (itera_desc,i i

/ i1)

FROM plsqll01_old_item;

Как показано на рис. 5.7, функция INSTR возвращает число, обозначающееположение запятой в каждом описании изделия. Мы могли бы использоватьэто число в качестве длины подстроки в функции SUBSTR, чтобы получить ка-тегорию изделия, но если так поступить, то в категорию будет включена запя-тая. Следовательно, нам нужно вычесть 1 из значения функции INSTR. Этодемонстрируется в следующем коде:

SELECT item_desc,

SUBSTR (item_desc,

1,INSTR (item_desc,

) -1. )

FROM plsql!01_old_item;

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

Встроенные функции SQL 131

Ж Oracle SQL'Plus

File Edit Search Qptbns tjelp

SQL> SELECT item_desc,2 INSTR(iten_desc,3U 15 .., )6 FROM plsql101_old_iten;

НИИ

ITEM DESC INSTR(ITEM_DESC,V ,1)

Can, Snail

Can, Large

Battle, Small

Bottle, Large

Box, Snail

Box, Large

Shipping Carton, Small

Shipping Carton, Large

8 rows selected.

SQL>

*77<t41616

Рис. 5.7. Результаты, возвращаемые функцией INSTR

* Oracle SQL'Plus

Fte Edit Search Options Help

SQL> SELECT item_desc,2 SUBSTR(item_desc,3 1,4 IHSTR(iten desc,56 17 ) -18 )9 FROM plsql1B1_old_iten;

Я -IE

ITEM DESC SUBSTR(ITEM_OESC,1,INSTR(

Can, Small

Can, Large

Bottle, Snail

Bottle, Large

Box, Snail

Box, Large

Shipping Carton, Snail

Shipping Carton, Large

8 rows selected.

SQL> |

iLJ

CanCanBottle

Bottle

BoxBox

Shipping Carton

Shipping carton

Рис. 5.8. Выделение подстроки переменной длины

132 Глава5

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

Но вернемся к нашей задаче — ведь из столбца ITEM_DESC еще не извле-чен размер изделия. Вся хитрость в том, чтобы найти начальную позицию раз-мера, требуемую для функции SUBSTR. Решение опять обеспечивает функцияINSTR, определяющая положение запятой. Но фактическое начало подстрокис размером сдвинуто относительно запятой на два символа, поэтому нужно до-бавить 2 к значению, возвращаемому функцией INSTR. Введите следующийкод, чтобы получить список размеров изделий:

SELECT item_desc,

SUBSTR(item_desc,INSTR(item_desc,

i tГ I

1) +2, '

99)

FROM plsqll01_old_item;

В данном случае длина подстроки не играет роли, поскольку вы извлекаетевесь текст, оставшийся в строке. Чтобы подчеркнуть этот факт, я указал в функ-ции SUBSTR длину 99.

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

SELECT item_desc,•SUBSTR(item_desc,

1,INSTR(item_desc,

I I/ t

1) -1

) CATEGORY,SUBSTR(item_desc,

INSTR(item_desc,t I

I t

1) +2,

99) ITEM_SIZE

FROM plsql!01_old_item;

Результаты выполнения этой команды показаны на рис. 5.9.Отступы, использованные в только что показанном коде, поясняют, как

сгруппированы части каждой функции. Такое форматирование полезно приизучении функций, поскольку оно помогает определить, с какой частью той

Встроенные функции SQL 133

К * Oiacle SQL'Plus

File Edit Search Qplions Help

SQL> SELECT item desc,2 SUBSTR(item desc,3 1.4 INSTR(item desc,

56 1

7 ) -18 ) CATEGORY,9 SU8STR(item desc,10 IHSTR(item desc.1112 113 ) +2,14 9915 ) ITEM SIZE16 FROM plsq!101_old_itero;

ITEM_DESC CftTEGORY

Can, Small CanCan, Large CanBottle, Small BottleBottle, Large BottleBox, Small BoxBox, Large BoxShipping Carton, Small Shipping CartonShipping Carton, Large Shipping Carton

8 rows selected.

SQL> |

.iLJ

Bra в

d

.

ITEM_SIZE

SmallLargeSmallLargeSmallLargeSmallLarge

, . . . , . . ,

26.

Рис. 5.9. Разбор на несколько подстрок переменной длины

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

1) -1) CATEGORY,

+2, 99) ITEM SIZE

SELECT item_desc,

SUBSTR(item_desc, 1, INSTR(item_desc, ',',SUBSTR(item_desc, INSTR(item_desc, ',', 1)

FROM plsql!01_old_item;

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

LTRIM и RTRIMЛучший способ объяснить, что делают эти функции, — привести пример

проблемы, которую они решают. Введите следующий код и сравните результа-ты с показанными на рис. 5.10.

134 Глава 5

IJH* Oracle SQL'PlusFile Ed* Search Options Help

SQL> SELECT 'Item ' | |2 item_id ||3 ' is described as a ' | |* item_desc | |5 '.' "Item Description Sentence"6 FROM plsql101_old_itera;

НЫВН

J

Item Description Sentence

ItemItemItemItemItemItemItemItem

8 rows

SQL> |

jJJ

Lfl-101Lfl-1 02Lfl-1 03Lfl-1 04NV-101NV-1 02NV-103NV-1 Oil

selected.

isisisisisisisis

describeddescribeddescribeddescribeddescribeddescribeddescribeddescribed

asasasasasasasas

--

aaaaaaaa

Can, SmallCan, LargeBottle, SmallBottle, LargeBox, SmallBox, LargeShipping Carton, SmallShipping Carton, Large

-

j51

Рис. 5.10. Конкатенация переменных типа CHAR

SELECT 'Item ' | |

item_id | |

' is described as a ' | |

item_desc | I

' . ' "Item Description Sentence"

FROM plsq!101_old_item;

Почему после значений ITEM_ID и ITEM_DESC так много пустого места?Причина в том, что оба столбца имеют тип CHAR. Помните, чем отличаютсятипы данных VARCHAR2 и CHAR? VARCHAR2 имеет переменную длину, тог-да как CHAR — фиксированную. Это означает, что сохраненные в столбце

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

На помощь опять приходят функции . Процесс удаления избыточных пробе-лов в начале или конце текстовой строки называется обрезанием (trimming), идля этого в Oracle есть две функции: LTRIM и RTRIM. Функция LTRIM удаля-ет пробелы в начале строки, а функция RTRIM — в конце. Они имеют одинако-вый синтаксис:

КТШМ(имя_столбца)

Встроенные функции SQL 135

В столбцах ITEM_ID и ITEM_DESC проблемой являются избыточные про-белы на концах строк. Решение этой проблемы обеспечит функция RTRIM.Достаточно подставить в нее имя каждого столбца, как показано ниже:

SELECT 'Item ' ||

RTRIM(item_id) ||' is described as a ' ||

RTRIM(item_desc) ||'.' "Item Description Sentence"

FROM plsq!101_old_item;

Введите этот код и сравните полученные результаты с теми, что показаны нарис. 5.11.

Теперь проверим, чему вы научились. Объедините рассмотренные вышефункции SUBSTR и INSTR с функцией RTRIM и напишите запрос к таблицеPLSQL101_OLD_ITEM, который бы выдавал результаты, показанные нарис. 5.12.

Для получения листинга, показанного на рис. 5.12, не требуется никаких но-вых знаний. Вам нужно лишь по-новому скомбинировать уже изученное. Я могбы привести возможный ответ, но не стал этого делать. Способ получения отве-та не играет роли, пока результаты соответствуют поставленной цели. Этоважно иметь в виду, приступая к работе в изучаемой области. Очень немногиелюди будут когда-либо рассматривать ваш код. Их будут интересовать исклю-чительно результаты. Убежден, что вы сможете получить результаты, показан-ные на рис. 5.12. Вероятно, потребуется несколько тестовых прогонов для

*. Oracle SQL'Plus

file £dit Search Options Help

SQL> SELECT 'Item ' | |RTRIM(item_id) ||' is described as aRTRIM(iten_desc) ||'.• "Item Description Sentence"plsql101_old_iten;

23Ч

5

6 FROM

и

Item Description Sentence

Item LA-101 is described as a Can, Small.Item LA-1D2 is described as a Can, Large.

Item LA-ЮЗ is described as a Bottle, Small.

Item Lfl-184 is described as a Bottle, Large.Item NV-101 is described as a Box, Snail.

Item NV-102 is described as a Box, Large.Item NV-103 is described as a Shipping Carton, Small.

Item NV-1B4 is described as a Shipping Carton, Large.

8 rows selected.

SQL>

Рис. 5.11. Обрезание текстовых значений фиксированной длины

136 Глава 5

File Edit Seated Option: Help

Item ID sentence

The Iten ID forThe Iten ID forThe Iten ID forThe Iten ID forThe Iten ID forThe Iten ID forThe Iten ID ForThe Iten ID for

8 rows selected.

SQL>

Snail Can is: 1.Й-101.Large Can is: Ld-102.Snail Bottle is: Lft-183.Large Bottle is: LA-181».Small Box is: NY-101.

Large Box is: NV-102.Snail Shipping Carton is: NV-1Q3.Large Shipping Carton is: HY-104.

Рис. 5.12. Отшлифуйте свои навыки, получив эти результаты!

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

Функции для работы с датамиВ Oracle есть ряд функций, предназначенных для облегчения работы с дата-

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

SYSDATE и TRUNCЧтобы познакомиться с проблемой, которую вам предстоит решать, введите

следующий код:

INSERT INTO plsql!01_product VALUES

('Square Zinculator', 45, 1, SYSDATE);

SELECT * FROM plsql!01_product; *

Пока все идет замечательно. Вы видите новую запись, все ее данные выгля-дят правильными. Теперь введите другой код, заменив dd-mmm-yy на текущуюдату (которая содержится в новой записи):

SELECT * FROM plsql!01_product

WHERE last_stock_date = 'dd-mrm-yy';

Если вы ввели эти команды правильно, в результате не будет выведено ниодной записи! Почему же не показана новая запись, когда вы ясно видели, чтоона содержит сегодняшнюю дату?

Встроенные функции SQL 137

Конечно, дело в том, что помещенная в эту запись дата была сгенерированапри помощи SYSDATE, a SYSDATE возвращает не только текущую дату, но итекущее время. Хотя время и не показано (очень скоро вы узнаете, как его выве-сти), оно все равно присутствует, в результате чего значение записи не совпада-ет с текущей датой. Это все равно что сравнивать 1 и 1.4 в столбце, форматкоторого не позволяет выводить дробную часть: различие есть, хотя оно и непоказано.

Решение таково: нужно использовать функцию TRUNC, чтобы в конструк-ции WHERE игнорировалось значение времени. Для этого вы должны подста-вить в функцию TRUNC ссылку на столбец LAST_STOCK_DATE, как вприведенном ниже операторе SELECT:

SELECT * FROM plsql!01_product

WHERE TRUNC(last_stock_date) = 'dd-mim-yy';

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

Альтернативный подход состоит в том, чтобы удалить временной компо-нент из даты, хранящейся в базе данных. В качестве примера введите показан-ные ниже команды, обратив внимание на то, что в операторе INSERT функцияSYSDATE является аргументом функции TRUNC.

DELETE FROM plsql!01_product

WHERE product_name = 'Square Zinculator';

INSERT INTO plsql!01_product VALUES

('Square Zinculator', 45, 1, trunc(sysdate));

SELECT *' FROM plsql!01_product

WHERE last_stock_date = 'dd-mmm-yy';

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

ADD_MONTHSФункция ADD_MONTHS возвращает дату с тем же днем месяца, что и в ис-

ходной дате, но отнесенную на заданное количество месяцев в будущее (илипрошлое). Эта функция имеет следующий синтаксис:

АОО_М(ЖТН8('иачо/гь«оя_дл/яа', количество_месяцев)

Начольноя_дата может представлять собой текущую дату (полученную с по-мощью TRUNC(SYSDATE)) или имя столбца таблицы. Количество_месяцев —это целое число, показывающее, сколько месяцев вы хотите добавить к на-

138 Глава 5

чальной дате или вычесть из нее. (Для вычитания месяцев нужно указатьотрицательное число.)

Если вы хотите увидеть, как работает эта функция, введите следующиекоманды:

SELECT ADD_MONTHS(SYSDATE,1) FROM DUAL;SELECT ADD_MONTHS(SYSDATE,12) FROM DUAL;

Функция ADD_MONTHS достаточно "интеллектуальна", чтобы определить,является ли указанный день последним днем месяца, и соответствующим обра-зом скорректировать конечный результат. Чтобы понять, о чем идет речь, введи-те следующие команды и сравните свои результаты с показанными на рис. 5.13:

SELECT ADD_MONTHS('28-NOV-00', 1) FROM DUAL;SELECT ADD_MONTHS('29-NOV-OO

1, 1) FROM DUAL;

SELECT ADD_MONTHS('30-NOV-00', 1) FROM DUAL;SELECT ADD_MONTHS('31-DEC-00', -1) FROM DUAL;

Обратите внимание, что в последней команде месяц вычитается из даты"31 декабря". Формально ответом будет "31 ноября", но в ноябре только 30 дней.Функция ADD_MONTHS это знает и учитывает при выдаче окончательногорезультата. Такая корректировка выполняется только для последнего дня меся-ца. Как видно по трем предыдущим командам, в остальных случаяхADD_MONTHS добавляет ровно один месяц.

Ж Oiacle SQL-Plus

fife £dk !SeachSQL> SELECT flDD_HONTHS('28-NOU-OO', 1) FROM DUftL;

ftDD_MOMTH

28-DEC-OO

SQL> SELECT flDD_MONTHS('29-NOU-B8', 1) FROM DUAL;

ADDJTOHTH

29-DEC-80

SQL> SELECT ftDD_MONTHS('Зв-НОи-вв', 1) FROH DUflL;

ADDJ40KTH

31-DEC-OB

SQL> SELECT flDD_MONTHS('31-DEC-OO', -1) FROM DUAL;

ADD_HONTH

Зв-NOU-OO

SQL>

Рис. 5.13. Использование функции ADD_MONTHS

Встроенные функции SQL 139

Эта функция может использоваться, например, при составлении планов,когда вам нужно сделать запись о необходимости что-либо проверить или сно-ва с кем-то связаться спустя месяц. Чтобы поместить в таблицу требуемую дату,можете включить в оператор INSERT или UPDATE следующей'

ADD_MONTHS(TRUNC(SYSDATE), 1)

LAST_DAYФункция LAST_DAY решает простую задачу, над которой пришлось бы не-

мало поработать при самостоятельном программировании: возвращает по-следний день любого месяца, указанного в переданной ей дате. Вот еесинтаксис: , , , , , , , , ,

LAST_DAY(9a/na)

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

SELECT LAST_DAY(SYSDATE) FROM DUAL;

SELECT LAST_DAY('01-JAN-02') FROM DUAL;

SELECT LAST~DAY('15-JAN-02') FROM DUAL;

SELECT LAST_DAY('31-JAN-02') FROM DUAL;

Функция этого типа применяется в целом ряде ситуаций. Например, вомногих компаниях медицинская страховка нового служащего начинает дейст-вовать с первого дня месяца, следующего за месяцем приема на работу. Если,скажем, один человек начинает работать с 1 апреля, а другой — с 30 апреля,страховки обоих будут действовать с 1 мая. Теперь подумайте: как модифици-ровать функцию, возвращающую последний день месяца, чтобы она возвраща-ла первый день следующего месяца?

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

CREATE TABLE plsql!01_person (

person_code VARCHAR2(3),

first_name VARCHAR2(15),

last_name VARCHAR2(20),hiredate DATE

INSERT INTO plsql!01_person VALUES

CCA', 'Charlene1, 'Atlas', 'Ol-FEB-02');

INSERT INTO plsql!01_person VALUES

('GA', 'Gary', 'Anderson', '15-FEB-02');INSERT INTO plsql!01_person VALUES

( 'BB', 'Bobby' , 'Barkenhagen1;, '28-FEB-02 ') ;

INSERT INTO plsql!01_person VALUES

('LB', 'Laren', 'Baxter', 'Ol-MAR-02');

140 Глава 5

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

\SELECT first_name,

last_name,

hire_date,LAST_DAY(hire_date)+1 INSURANCE_START_DATE

,FROM plsqlldljperson;

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

t Oracle SQLTIus

File Edit Search Options HelpSQL> SELECT f irst_nane ,

НЙЕЗ

2

3

*5 FROM

FIRST NAME

lastjname,hire date,LAST~DflV(hire_date)+1 INSURflNCE_START_DATEplsqll81_person;

d

LftST_NflME HIRE DATE INSURANCE

CharleneGaryBobbyLaren

SQL>

AtlasAndersonBarkenhagenBaxter

B1-FEB-82 81-MAR-8215-FE8-82 81-MAR-B228-FEB-82 B1-MAR-B2ei-HflR-82 81-APR-82

Рис. 5.14. Использование функции LAST_DAY для определения первогодня следующего месяца

Вкладывая функции для работы с датами друг в друга, можно получитьдовольно интересные результаты. Рассмотрим такой пример: каждая за-пись в таблице PLSQL101_PRODUCT содержит дату последнего пополне-ния запаса товара на складе. Допустим, в некой компании, где выработаете, пополнение запасов должно производиться каждые три месяца.Чтобы получить очередную дату, можно было бы использовать функциюADD_MONTHS, но есть осложняющее обстоятельство: товары заказыва-ются только первого числа каждого месяца. Таким образом, сначала вамнужно отсчитать три месяца вперед, а потом перейти на начало следующегомесяца. Как это сделать?

Очевидно, объединив два только что изученных приема: добавление задан-ного числа месяцев к дате и использование функции 1А8Т_ОА¥для полученияпоследнего дня месяца с последующим добавлением к нему единицы. В итогесинтаксис вложенных функций примет следующий вид:

Встроенные функции SQL 141

LAST_DAY(ADD_MONTHS(

столбец_с_датой_последнего_пополнения,интервал_пополнения

+ 1

Просматривая приведенный ниже код, обратите особое внимание на чет-вертую строку, в которой только что показанный синтаксис используется состолбцом LAST_STOCK_D ATE и трехмесячным интервалом. Для большей ре-алистичности в этом примере выводятся записи только о тех товарах, запас ко-торых подходит к концу, и эти записи сортируются так, чтобы названия товаровшли в алфавитном порядке. Теперь введите этот код и сравните результаты споказанными на рис. 5.15.

SELECT product_name,quantity_on_hand,'last_stock_date,LAST_DAY(ADD_MONTHS(last_stock_date, 3))+l RESTOCK_DATE

FROM plsql!01_productWHERE quantity_on_hand <= 100ORDER BY product_name ;

MONTHS_BETWEENMONTH_BETWEEN — это совсем простая функция, которая возвращает

количество месяцев, разделяющих две даты. Она имеет следующий синтаксис:

MONTHS_BETWEEN(/c0«e4HOH_daffJfl, начальная_дата)

*. Oiacle SQL-Plus

£Je Ed» Search Options HelpSQL> SELECT product_nane,

2 quantity_on_hand,3 last_stock_date,Ц LAST DftV(ftDD_MONTHS(last_stOCk_date, 3))+1 RESTOCK_DflTE

5 FROM plsqll81_product6 WHERE quantity_on_hand <= 1007 ORDER BV product_nane;

ИИЕЗ

PRODUCT NftME QUANTITV_ON_HAND LAST_STOC RESTOCKJ)

Chrome PhoobarSnail WidgetSquare Zinculator

SQL> |

-iLJ

100 15-JAN-03 01-HAV-03

1 15-JAN-03 01-MAV-03

1 09-JUL-OO 01-NOU-OO

Рис. 5.15. Вложение функций для работы с датами

142 Глава 5

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

SELECT product_name,last_stock_date,MONTHS_BETWEEN(SYSDATE, last_stock_date) STOCK_MONTHS

FROM plsqll01_product;

Чтобы значение интервала было немного легче читать, можно округлитьзначение функции MONTH_BETWEEN, использовав функцию ROUND:

SELECT product_name,last_stock_date,

ROUND(MONTHS_BETWEEN(SYSDATE, last_stock_date),0) STOCK_MONTHS

FROM plsql!01_product;

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

SELECT MONTHS_BETWEEN(SYSDATE, дата_рождения) FROM DUAL;

Представляете, какую уйму месяцев вы прожили? Полагаю, что остатокэтой главы теперь покажется вам чем-то вроде легкой прогулки по парку.

Функции преобразования данныхПод преобразованием данных (data conversion) понимается преобразование

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

• Они позволяют изменять способ отображения дат, времен и чисел.

• Они упрощают импорт данных из других источников.

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

TO_CHARФункция TO_CHAR преобразует дату, время или число в текст. Ее основная

ценность в том, что она позволяет в широких пределах управлять отображени-ем дат, времен и чисел. Тот факт, что все это будет показано в текстовом виде,не имеет значения при просмотре на экране SQL*Plus.

Возможно, вы заметили, что к выбранным из таблицы числам не применя-ется какое-либо определенное форматирование; они отображаются с тем коли-чеством десятичных знаков, которое содержат, поэтому список не всегдабывает выровнен. Определенные проблемы связаны и с датами: они отобража-ются в таком формате, который мало кто из нас использует в повседневнойжизни, и по умолчанию не содержат времени. Функция TO_CHAR позволяетисправить обе эти ситуации.

Встроенные функции SQL 143

Форматирование значений даты и времени Синтаксис функции TO_CHAR,предназначенной для изменения способа отображения дат и времени, имеетследующий вид:

ТО_СНАК(входное_значение, код_формата)

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

Простой пример использования функции TO_CHAR приведен ниже.

SELECT TO_CHAR(SYSDATE, .'MM-DD-YYYY HH24:MI:SS') NOW

FROM DUAL;

Результаты, выданные SQL*Plus, говорят о том, что в данном примерекод_формата задает отображение текущей даты в привычном большинству лю-дей формате, а также позволяет (наконец-то!) увидеть временной компонентSYSDATE. Вы можете составить любой код формата, комбинируя элементы всоответствии со своими потребностями. Доступные элементы перечислены втаблице 5.3. Возможно, что после просмотра этой таблицы вы будете удивлены,каким невероятным количеством способов Oracle может отображать дату и время.

Элемент формата DD, представляющий день месяца, имеет два необязате-льных суффикса, которые позволяют внести некоторые косметические улуч-шения. Если добавить суффикс ТН, Oracle будет показывать "1ST" вместо "1","2ND" вместо "2", и т.д. Чтобы увидеть, как это выглядит, введите следующуюкоманду:

SELECT TO_CHAR(SYSDATE, 'MONTH DDTH1)

FROM DUAL;

При добавлении суффикса SP день будет представлен в буквенном написа-нии. В качестве примера введите следующий код:

SELECT TO_CHAR(SYSDATE, 'MONTH DDSP')

FROM DUAL;

В результате объединения этих двух суффиксов Oracle будет расшифровы-вать день месяца и добавлять после него "st", "nd", "rd" или "th". Это демонстри-руется следующей командой:

SELECT TO_CHAR(SYSDATE, 'MONTH DDSPTH1)

FROM DUAL;

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

• Лишние пробелы после названий месяцев

6 Зак. 725

144 Глава5

Таблица 5.3. Элементы кода формата даты и времени, используемогов функции TCLCHAR

Элемент Значение

Воспроизведение соответствующих знаков препинания и текста/ в кавычках

'любой текст'

AD Обозначение года новой эры (с точками или без точек)АО.

AM Обозначение времени до полудня (с точками или без точек)А.М.

ВС Обозначение года до новой эры (с точками или без точек)В Р.L».

СС Век (для четырехзначного года); в варианте с "S" даты до н. э.SCC предваряются знаком"-"

D День недели (1-7)

DAY Название дня недели, дополненное пробелами до девятисимволов

' ' • ! ,'.. . '• . ' : - •", ' ..

DD День месяца (1-31)

DDD День года (1-366)

DY Сокращенное название дня недели

Е Сокращенное название эпохи (для японского имперского,тайско-буддийского и официального китайского календарей)

ЕЕ Полное название эпохи (для японского имперского,тайско-буддийского и официального китайского календарей)

НН Часдня(1-12)

НН12 . Часдня(1-12)

НН24 Час дня (0-23)

IW Неделя года (1 -52 или 1 -53) согласно стандарту ISO

Встроенные функции SQL 145

Таблица 5.3 (продолжение)

Элемент Значение

IYYY Четырехзначный год согласно стандарту ISO- - . . .

IYY Последние цифры (три, две или одна) года ISOIY

. -

J Юлианский день; количество дней, прошедших с 1 января 4712 г.до н.э. Число со спецификатором J должно быть целым

• . • . - • • ; • . • : • : .1

Ml . Минуты (0-59)

М Месяц (01-12; январь = 01)

MON Сокращенное название месяца

MONTH Название месяца, дополненное пробелами до девяти символов

РМ Обозначение времени после полудня (с точками или без точек)P.M.

/

Q Квартал года (1,2,3,4; январь-март = 1)

RM Римский номер месяца (I-XII; январь = I)

RR Для года, записанного двумя цифрами, возвращает год следующеговека, если год < 50 и две последние цифры текущего года >= 50;возвращает год предыдущего века, если год >= 50 и две последниецифры текущего года < 50

RRRR На входе допустимы четыре или две цифры. Для двух цифр возвраща-ет тот же результат, что и RR. Если вам не нужна эта возможность,просто вводите четырехзначный год

SS Секунды (0-59)

SSSSS Секунды после полуночи (0-86399)' : ' ' . . . ' . , ' ' /:'

WW Неделя года (1 -53), в котором первая неделя начинается в первыйдень года и продолжается до седьмого дня года

W Неделя месяца (1 -5), в котором первая неделя начинается в первыйдень месяца и заканчивается в седьмой

Y.YYY Год с запятой в указанной позиции

146 Глава 5

Таблица 5.3 (.продолжение)

Элемент Значение) . •

YEAR Год в буквенном написании; в варианте с "S" даты до н.э. предваря-SYEAR ются знаком"-"

Y Y Y Y Год из четырех цифр; в варианте с "S" даты до н.э. предваряютсяSYYYY знаком"-"

Y Y Y Последние цифры года (три, две или одна)Y YY

• Прописные буквы в названиях месяцев

• Прописные буквы в суффиксе после числа месяца (например, "ТН"вместо "th").

• Прописные буквы в числе месяца при использовании суффикса SP

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

SELECT TO_CHAR(SYSDATE, 'MONTH DDTH1)

FROM DUAL;

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

SELECT TO_CHAR(SYSDATE, ' M O N T H ' ) | |I ' I ITO_CHAR(SYSDATE, ' D D T H ' )

FROM DUAL;

Теперь можно заняться удалением лишних пробелов. Подставьте функциюTO_CHAR в функцию RTRIM, как показано ниже:

SELECT RTRIM(TO_CHAR(SYSDATE, ' M O N T H ' ) ) | |I I I ITO_CHAR(SYSDATE, ' D D T H ' )

FROM DUAL;

Это даст желаемый интервал. Далее вам нужно изменить регистр символов вназвании месяца, чтобы только первая буква была прописной. ФункцияINITCAP делает именно это. Подставьте в нее часть оператора SELECT, отно-сящуюся к названию месяца, как показано ниже:

Встроенные функции SQL 147

SELECT INITCAP(RTRIM(TO_CHAR(SYSDATE, 'MONTH'))) I I• i | |

TO_CHAR(SYSDATE, ' DDTH ' )

FROM DUAL;

Последнее изменение касается регистра символов в суффиксе после дня ме-сяца. Держу пари, что у вас уже есть идея, как это сделать. Правильно: нужноиспользовать функцию LOWER, как показано в этом коде:

SELECT INITCAP(RTRIM(TO_CHAR(SYSDATE, ' M O N T H ' ) ) ) I I' ' I ILOWER ( TO_CHAR ( S YSDATE , ' DDTH ' ) )

FROM DUAL;

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

SELECT TO_CHAR(SYSDATE, 'MONTH DDSPTH')

FROM DUAL;

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

i . • :j SELECT * FROM plsqll01_product;

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

SELECT product_name,

TO_CHAR(product_price, '$9,9-99.00') "Price",

quant ity_on_hand,

last_stock_date

FROM plsql!01_product;

Полученные результаты должны выглядеть так, как показано на рис. 5. 16.Как и форматы дат, форматы чисел составляются из одного и более элемен-

тов, каждый из которых отвечает за определенный аспект форматирования.Эти элементы приведены в таблице 5.4. Чтобы попрактиковаться в их примене-нии, составьте оператор SELECT, выводящий столбец QUANTITY_ON_HANDтаблицы PLSQL101_PRODUCT в более удобочитаемом виде. Заодно стоит пе-ределать и столбец LAST_STOCK_DATE. Попробуйте написать команды так,чтобы их выходные данные совпали с показанными на рис. 5.17. Обратите вни-мание, что столбец LAST_STOCK_DATE смещен на две позиции вправо, что-бы его было легче визуально отличить от столбца QUANTITY_ON_HAND.Подскажу, что для этого используется конкатенация.

TO_DATEФункция TO_DATE преобразует текстовое представление даты (и/или вре-

мени) в действительные значения даты/времени. Хотя ее основное назначе-ние — импорт текстовых файлов с датами и временем из других баз данных, онатакже полезна при ручном вводе даты в формате, отличном от стандартного

148 Глава5

Таблица 5.4. Элементы кода формата чисел

Элемент

$

,(запятая)

.(точка)

Ml

PR

G

С

RN или rn

О

О

Пример

$9999

9,999

99.99

9999MI

S9999

9999PR

99D99

С999

L999

RN

0999

9990

Описание

Помещает знак доллара перед значением

Помещает запятую в указанной позиции

Помещает десятичную точку в указанной позиции

Отображает знак"-" после отрицательного числа

Помещает в указанной позиции знак"+" (для по-ложительных чисел) или знак"-" (для отрицатель-ных чисел)

Окружает отрицательные числа <угловыми скоб-ками>

Отображает в указанной позиции десятичный раз-делитель, принятый в вашей стране

Отображает в указанной позиции разделительгрупп разрядов, принятый в вашей стране

Отображает знак денежной единицы ISO в указан-ной позиции

Отображает знак местной денежной единицы вуказанной позиции

Отображает числа римскими цифрами верхнегоили нижнего регистра (только для целых чисел от1 до 3999)

Отображает один и более начальных нулей

Отображает пустые значения как нули

формата Oracle (DD-MON-YY), или при вводе времени. Эта функция имеетследующий синтаксис:

ТО_ОАТЕ(входное_значение, код_формата)

Функция TO_DATE использует подмножество элементов кода формата,определенных в функции TO_CHAR. Элементы, входящие в это подмножест-во, перечислены в таблице 5.5.

Встроенные функции SQL 149

Таблица 5.5. Элементы кода формата даты и времени

Элемент Значение

Воспроизведение соответствующих знаков препинания и текста/ в кавычках

'любой текст'

ADA.D.

А.М.

ВСВ.С.

D

DAY

DO

ODD

DY

HH

HH24

J

Ml

MM

MON

MONTH

PMP.M.

Обозначение года новой эры (с точками или без точек)

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

Обозначение года до новой эры (с точками или без точек)

День недели (1-7)

Название дня недели, дополненное пробелами до девяти символов

День месяца (1-31)

День года (1-366)' ...; .

Сокращенное название дня недели

Час дня (1-12)

Час дня (0-23)

Юлианский день; количество дней, прошедших с 1 января 4712 г. дон.э. Число со спецификатором J должно быть целым

Минуты (0-59)

Месяц (01-12; январь = 01)

Сокращенное название месяца

Название месяца, дополненное пробелами до девяти символов

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

150 Глава 5

Таблица 5.5 (продолжение)

Элемент

RM

RR

RRRR

SS

SSSSS

Y.YYY

Y Y Y Y' SYYYY

Y Y YY YY

Значение

Римский номер месяца (I-XII; январь = I)

Для года, записанного двумя цифрами, возвращает год следующеговека, если год < 50 и две последние цифры текущего года >= 50;возвращает год предыдущего века, если год >= 50 и две последниецифры текущего года < 50

На входе допустимы четыре или две цифры. Для двух цифр возвраща-ет тот же результат, что и RR. Если вам не нужна эта возможность,просто вводите четырехзначный год

'..Секунды (0-59)

Секунды после полуночи (0-86399)

Год с запятой в указанной позиции

Год из четырех цифр; в варианте с "S" даты до н.э. предваряются зна-ком "-"

/ .

Последние цифры года (три, две или одна)

Чтобы увидеть, насколько гибко функция TO_DATE позволяет вставлятьдату и время, введите следующий код и сравните результаты с показанными нарис. 5.18.

SELECT product_name,product_price,quantity_on_hand,TO_CHAR(last_stock_date, 'MM-DD-YYYY H H 2 4 : M I ' ) "Last Stocked"FROM plsql!01_product;

UPDATE plsql!01_productSET last_stock_date = TO_DATE('December 31, 2002, 11:30 P . M . ' ,

'Month dd, Y Y Y Y , HH:MI P . M . ' )WHERE product_name LIKE '%Zinc%' ;

SELECT product_name,product_price,quantity_on_hand,TO_CHAR(last_stock_date, 'MM-DD-YYYY HH24:MI') "Last Stocked"

FROM plsql!01_product;

Встроенные функции SQL 151

• J: Oracle SQL-PlusFile Edit Search Options Help

SQL> SELECT product name,2 TO_CHAR(product_price, '$9,999.88') "Price",3 quantity on_hand,4 last stock date5 FROM plsq!181_product;

• -Ш1 x|

d

PRODUCT_NftME Price QUANTITV_ON_HAHD LAST_STOC

Small WidgetMedium WodgetChrome PhoobarRound Chrome SnaphooExtra Huge Mega Phoobar +Square Zinculator

6 rows selected.

SQL>

iLJ

$99.88 1 15-JAN-83$75.88 1888 15-JAN-82$58.88 100 15-JAN-83$25.88 18888

$9.95 1234 15-JAN-84$45.88 1 89-JUL-88

V

• •

лП

Рис. 5.16. Использование функции TO_CHAR для улучшения внешнеговида чисел

• *. Oracle SQL-PlusFile £dit Search Qptions Help

PRODUCT_NAME Price

Small WidgetMedium WodgetChrome PhoobarRound Chrome SnaphooExtra Huge Mega Phoobar +Square Zinculator

6 rows selected.

SQL>jJJ

$99$75$58$25

$9$45

On Hand Last Stocked

.88 1 JAN 15, 2883

.88 1,888 JAN 15, 2882

.88 188 JAM 15, 2883

.88 18,000

.95 1,234 JAN 15, 2884

.88 1 JUL 89, 2888

,

ДИДДН I' 'I v|.

-

.

JРис. 5.17. Выходные данные, отформатированные различными

функциями TO_CHAR

Прочие функцииВ этом разделе представлена группа SQL-функций, которые объединяет

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

152 Глава5

A Oracle SQL-Plus И RES

fte Ed* !£e«ch :ДрИога:Ые1р r;;; V 'SQL> SELECT product_nane,2 product_price.3 quantity_on hand,it TO CHARUast stock date, 'MM-DD-YVVV НН24:МГ) "Last Stocked"5 FROM plsql101_product;

PRODUCT_NftME PRODUCT_PRICE QUfiNTITV_ON_HflND Last Stocked

Small WidgetMedium WodgetChrone PhoobarRound Chrome SnaphooExtra Huge Mega PhoobarSquare Zinculator

6 rows selected.

99755025

9.9545

1 01-15-2003 BO:081B00 01-15-2802 00:00

100 01-15-2003 00:0010000

. 1234 01-15-2004 00:001 07-09-2000 00:00

SQL>SQL> UPDATE plsqHQl product2 SET last_stock_date - TO DATECDecember 31, 2002, 11:30 P.M.'.3 'Month dd, VVVV, HH:MI P.M.')4 WHERE productjiame LIKE '%ZincV;

1 row updated.

SQL>SQL> SELECT product_name.

2 product_price,3 quantity_on_hand,4 TO CHAR(last stock date, 'MM-DO-vvvv HH24:MI') "Last Stocked"5 FROM plsql101_product;

PRODUCT NAME PRODUCT_PRICE QUAHTITV_ON_HAND Last Stocked

Small WidgetMedium WodgetChrome PhoobarRound Chrbme SnaphooExtra Huge Mega PhoobarSquare Zinculator

б rows selected.

SQL>

99755025

9.9545

1 01-15-2003 00:001000 01-15-2002 00:00

100 01-15-2003 00:0010000

1234 01-15-2004 00:001 12-31-2002 23:3B

Рис. 5.18. Использование функции TO_DATE для гибкой вставкидат и времени

DECODEОдним из различий между языком SQL, который вы изучаете сейчас, и язы-

ком программирования PL/SQL, к изучению которого вы скоро приступите,является то, что SQL не позволяет управлять последовательностью выполне-ния команд — в нем нет циклов, операторов "goto", if-then-else. Сценарий SQL

Встроенные функции SQL 153

выполняется сверху вниз, строка за строкой, без ветвлений. В SQL отсутствуютсредства принятия решений, но есть одна функция, котораяв чем-то аналогич-на оператору if-then-else: DECODE. Эта функция транслирует од но множестводанных в другое, используя определенные вами значения "до" и "после". Оха-рактеризовать ее работу можно так: если входное значение равно "А", на выходебудет "В", а если входное значение равно "С", функция вернет "D". Значения"А", "В", "С" и "D" вы определяете сами. Это может оказать неоценимую по-мощь при трансляции данных из одной системы баз данных в другую, посколь-ку разные системы обычно используют разные множества кодов дляпредставления схожей информации.

Функция DECODE имеет следующий синтаксис:

DECODE(ucmo4HUK_exodHbtx_daHHbix,входное'__знанение_1, выходное_значение_ 1,входное_значение_2, выходное_значение_2,

последнее_входное_значение, последнее_выходное_значение,[выходное_значение_по_умолчанию при_отсутствйи_совпадений])

Несколько пояснений к только что приведенному синтаксису:

• Указав источник_входных_данных (обычно это имя столбца некоторойтаблицы), вы определяете пары входных/выходных значений. Слеварасполагается одно из значений, которые будут поступать из источникаданных, а справа — то, во что вы хотите транслировать это значение.Можно определить сколько угодно таких пар (именно об этом говоритмноготочие между строками).

• Определив пары входных/выходных значений, вы можете отдельноуказать конечный результат, которому не соответствует какое-либоопределенное входное значение. Он будет возвращен в том случае, еслифункция DECODE встретит значение, отсутствующее в списке (поаналогии с блоком else оператора if-then-else). Эта часть функциинеобязательна (поэтому и заключена в квадратные скобки), но обычноее стоит указывать, чтобы знать, не встретились ли среди входныхданных какие-нибудь значения, которых вы не ожидали.

Чтобы продемонстрировать работу функции DECODE, предположим, чтовам дана таблица PLSQL101_OLD_ITEM и поручено выбрать из нее некото-рый набор записей. Однако со времени использования старых изделий струк-тура вашей компании изменилась, и теперь вместо идентификации изделий погороду, откуда они поступили, необходимо показывать регионы. Изделия,идентификатор которых содержит "NY1, относятся к восточному региону("Eastern"), а изделия "LA" поступают из западного ("Western") региона. Трех-значные номера, следующие за сокращенными названиями городов, по-преж-нему должны отображаться, поэтому вам придется использовать функциюSUBSTR, чтобы выделять подстроки с этими номерами. Ниже показано, какиспользовать функцию DECODE для трансляции городов в регионы. Про-смотрите и запустите этот код. Результаты должны совпасть с показанными нарис. 5.19.

154 Глава 5

Щ * Oiacle SQL-Plus ННИЕ

File Edit Search 'Options HelpSQL> SELECT DECODE(SUBSTR(iten_id, 1,2), -d

2 'Lfl1 , 'Western' , -J34567

'NV , 'Eastern' ,'* Unknown »') "Region",

SUBSTR(iten_id, 4,3) "Item ID",item desc

8 FROM plsql181_old_iten;

Region

WesternWesternWesternWesternEasternEasternEasternEastern

8 rows

SQL> |

jJJ

Ite ITEMJDESC

181 Can, Snail182 Can, Large183 Bottle, Snail184 Bottle, Large181 Box, Snail182 Box, Large183 Shipping Carton, Snail104 Shipping Carton, Large

selected.

• ' :

jJ^

-

Рис. 5.19. Использование функции DECODE для трансляции данных

SELECT DECODE(SUBSTR(item_id, I, 2),

'LA', 'Western',

'NY', 'Eastern',

'* Unknown *'

) "Region",

SUBSTR(item_id, 4,3) "Item ID",

item_desc

FROM plsqll01_old_item;

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

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

• Старая система хранила даты в стандартном формате Oracle, но в новойсистеме они должны были вставляться как текстовые строки форматаYYYYMMDD.

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

Встроенные функции SQL 1 55

дефисы. Затем результирующей строке присваивалось имяPATIENTJD.

• В старой системе для обозначения пола пациентов использовалиськоды М (Male, мужской), F (Female, женский), О (Other, другой —клиент был из Лос-Анджелеса) и U (Unknown, неизвестный). В новойсистеме эти значения требовалось представить как 1 , 2, 3 и 4.Классическая задача, решаемая с помощью DECODE.

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

• Многие из полей в старой системе были заполнены неаккуратно, слишними пробелами до и/или после данных. Это было исправленопутем применения функций LTRIM и RTRIM к проблемным столбцам.

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

SOCIAL_SECURITY_NUMBER AGEJTYPE AGE_AT_ENTRY

<£О

222334444 Y 65

333445555 М 18

444556666 D 3

555667777 D 10

в новую схему такого вида:

PATIENTJD AGE_YRS AGE_MOS AGE_DAYS

111-22-3333 28

222-33-4444 65

333-44-5555 18

444-55-6666 3

555-66-7777 , 10

156 •'.,••. Глава5

Пример: Использование изученных функцийдля решения реальной задачи

select to_char(EXIT_DATE, 'YYYYMMDD')DISCHARGE_DATE,

substr(SOCIAL_SECURITY_NUMBER,1,3) I Г -'I I

substr(SOCIAL_SECURITY_NUMBER,4,2) I | ' - ' H •substr(SOCIAL_SECURITY_NUMBER,6,4) PATIENT_ID,

decode(GENDER,

'M', '!',

'F', '2',

'0', '3',1U', '4') GENDER,

substr(ZIP_CODE, 1,5) ZIPCODE,

decode(Itrimfrtrim(SITE) ) ,'300' 'LA

1

---'•

'350'4 10'

' 4 2 0 ''4,40 '

'ОС'LA 1

'ОС'' L A 1

' * N / A * ' ) COUNTY,I t r i m f r t r i m ( P H Y S I C I A N ) ) PHYSICIANto_char(DATE_OF_BIRTH, ' Y Y Y Y M M D D ' ) ; . DOB,decode(AGE_TYPE,

' Y 1 , AGE_AT_ENTRY,! l ) AGE_YRS,

decode(AGE_TYPE,' M ' , AGE_AT_ENTRY,1 ' ) AGE_MOS,

decode(AGEJTYPE,' D ' , AGE_AT_ENTRY,'') AGE DAYS,

from ENCOUNTER

Это было выполнено с помощью трех различных функций DECODE. Пер-вая просматривала столбец AGEJTYPE старой системы и, встречая там "Y", по-мещала значение AGE_AT_ENTRY в столбец AGE_YRS новой системы. Приотсутствии "Y" в столбце AGEJTYPE она оставляла столбец AGE_YRS пустым.Две других функции делали то же самое, отыскивая в столбце AGEJTYPE зна-чения "М" и "D" и помещая значения AGE_AT_ENTRYB столбцы AGE_MOS иAGE_DAYS, соответственно. Эти функции DECODE иллюстрируют пару ин-тересных фактов: во-первых, на данный столбец может ссылаться более однойфункции DECODE, а во-вторых, функция DECODE может возвращать значе-ние столбца, отличного от того, который использовался для принятия решения.

NVLФункция NVL выполняет простую, но важную задачу: получив в качестве

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

Встроенные функции SQL 157

~№У]-.(входное^значение,результат_если_пи1Г)

Как и во многих других функциях, входное_значение обычно представляетсобой имя столбца. Резулыпат_если_пи11 может быть всем, чем угодно: литера-лом (т.е. жестко закодированным значением), ссылкой на другой столбец илипроизвольным выражением.

Чтобы увидеть функцию NVL в действии, введите показанный ниже код.Результаты его выполнения должны быть такими, как на рис. 5.20.

SELECT product_name,

last_stock_date

FROM plsqllOljproduct;

SELECT product_name,NVL(last_stock_date,

FROM plsqll01_product;'Ol-JAN-2001') "Last Stocked"

* Oiacle SOL'HIus НИЕЗ0e EA Search Dions' Це1р " :

SQL> SELECT product_name,2 last stock_date3 FROM plsql101_product;

PRODUCT NAME LAST STOC

Small Widget 15-JAN-03Medium Uodget 15-JAN-02Chrome Phoobar 1S-JAN-03Round Chrome SnaphooExtra Huge Mega Phoobar * IS-JAN-OHSquare Zinculator 31-DEC-02

б rows selected.

SQL>SQL> SELECT product name,

2 NUL(last_stock date, 1ei-JAH-2BOl

1) "Last Stocked"

3 FROM plsql101_product;

PRODUCT NAME Last Stoc

Small WidgetMedium WodgetChrome PhoobarRound Chrome SnaphooExtra Huge Mega PhoobarSquare Zinculator

6 rous selected.

SQL> |

15-JAN-0315-JAN-0215-JAN-0301-JAN-0115-JAN-Oil31-DEC-02

Рис. 5.20. Функция NVL заменяющая null-дату на дату по умолчанию

158 Глава 5

Как видите, функция NVL заменила пустое значение LAST_STOCK_DATEуказанной датой. Вы могли бы заменить null на текущую дату, использовав сле-дующий код:

'SELECT product_name,

NVL(last_stock_date, T R U N C ( S Y S D A T E ) ) "Last Stocked"FROM plsqll01_product;

J«f Примечаниеp < g В действительности функция NVL не обновляет никаких значений.

Исходные данные таблицы остаются нетронутыми.

У функции NVL есть одна особенность: она ожидает, что входное_значение ирезулыпат_если_пи11 будут иметь один и тот же тип данных. Если входное_значе-ние — дата, то результат _если_пи11 также должен быть датой, и т,д. Это стано-вится проблемой, если вы хотите выводить популярное "N/A" дляnull-значений, поскольку "N/A" является текстом. Когда столбец, полученныйфункцией в качестве входного_значения, имеет текстовый тип, все в порядке.Но если функция проверяет на наличие null-значений числовой столбец илистолбец с датами, то вам потребуется подставить имя столбца в функциюTO_CHAR, чтобы входное значение также было текстом. Это демонстрируетсяв приведенном ниже коде. Первая команда завершится выдачей сообщения обошибке, тогда как вторая будет успешно выполнена. Введите код и сравните ре-зультаты с показанными на рис. 5.21.

SELECT product_name,

NVL last_stock_date, 'N/A') "Last Stocked"

FROM plsqll01_product;

SELECT prod'-uct_name,

NVL TO_CHAR(last_stock_date), 'N/A') "Last Stocked"FROM plsqll01_product;

Вставка комментариев в SQL-сценарииВ главе 4 обсуждалось, как создаются и запускаются файлы SQL-сценариев.

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

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

Встроенные функции SQL 159

Ж Oracle SQL-Plus

fjle Ed» Seatch Options Help

SQL> SELECT product_nane,2 NUL(last_stock_date, 'N/fi') "Last Stocked"3 FROM plsql101_product;

HUL(last_stock_date, 'N/fl') "Last Stocked"*

ERROR at line 2:

ORA-01858: a non-numeric character was found where a numeric was expected

SQL>

SQL> SELECT product_nane,

2 NUL(TO_CHflH(last_stock_date), 'H/fl') "Last Stocked"3 FROM plsqltei_product;

PRODUCT NAME Last Stoc '

Small Midget 15-JflN-03

Medium Wodget 15-JHN-B2

Chrome Phoobar 15-JflN-03

Round Chrome Snaphoo H/fl

Extra Huge Mega Phoobar + IS-JAN-eft

Square Zinculator 31-DEC-B2

б rows selected.

SQL>

Рис. 5.21. Использование функции NVL с разными типами данных

Чтобы создать однострочный комментарий, просто поставьте в начале стро-ки два дефиса. Все, что последует за дефисами, Oracle будет игнорировать. Вве-дите, например, следующий код:

SELECT * FROM plsql!01_product;-- Эта строка игнорируется. Oracle не будет пытаться ее выполнить.

SELECT *, FROM plsql!01_purchase;

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

[

> г Совет, - Для создания однострочного комментария можно поместить

в начале строки "REM" вместо "—". Этот способ используетсяв других языках программирования, поэтому знаком программистам,переходящим к изучению SQL после других языков. Однако чистовизуально "НЕМ" не привлекает внимание столь же легко, как "--".Возможно, именно поэтому вариант "—" более распространен.

Если вы намерены сгруппировать несколько строк комментариев, болееподходящим может оказаться другой подход, при котором в начале секциикомментария помещается "/*", а в конце — "*/". Все строки между этими пара-ми символов игнорируются. В качестве примера введите следующий код:

160 Глава 5

* Шлг1е SfJl'Plus

£Je £d» Search Options JHelpSQL> SELECT » FROM plsql101_product;

PBODUCT_NftME PRODUCT_PRICE QUflNTITV_OH_HflND LAST_STOC

Small WidgetMedium UodgetChrome PhoobarRound Chrome SnaphooExtra Huge Mega PhoobarSquare Zinculator

6 rows selected.

99755025

9.9545

1 15- JAN- 031000 15-JftN-B2100 15-JAN-83

100001234 15-JflN-flJt

1 31 -DEC- 02

SQL> — This line will be ignored. Oracle will not try to run it.SQL> SELECT « FROM plsql1D1_purchase;

PRODUCT_NAME QUANTITY PURCHASE. SAL

Small WidgetMedium UodgetChrome PhoobarSmall WidgetMedium UodgetRound Snaphoo

6 rous selected.

SQL>

1 14-JUL-03 CA75 14-JUL-03 BB2 14-JUL-03 GA8 15-JUL-03 GA

20 15-JUL-03 LB5 16-JUL-03 CA

Рис. 5.22. Однострочный комментарий в SQL-сценарии

Этот сценарий демонстрирует использование многострочных комментариев.Он написан специально для книги PL/SQL 101 издательства Oracle Press.*/

SELECT * FROM plsql!01_product;

SELECT * FROM plsql!01_purchase;

Результаты его выполнения показаны на рис. 5.23.'• '. '

Часто используемыегрупповые функции

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

Встроенные функции SQL 161

* Oracle SQL'Plus

£ile Edit Seatch Oplions HelpSQL> /»DOOThis script is designed to shou how multiple-line commenting works.DOOIt is used in the PL/SQL 1191 book by Oracle Press.BOO*/SQL>SQL> SELECT » FROM plsql1fl1_product;

ЯНЕЗ

PRODUCT NftHE PRODUCT_PRICE QUflHTITV_OH_HflHD Lfl$T_STOC

Snail Widget 99Medium Uodget 75Chrome Phoobar 50Round Chrome Snaphoo 25Extra Huge Mega Phoobar + 9.95Square Zinculator 45

б rows selected. ....-,,.

SQL>SQL> SELECT » FROM plsq!101_purchase;

1 15-JftN-e310QO 15-JSN-82

160 1S-JflN-03ieeee

1234 15-JflN-B41 31-DEC-82

PRODUCT NftME QUfiHTITV PURCHASE. SflL

Small WidgetMedium UodgetChrome PhoobarSnail WidgetMedium UodgetRound Snaphoo

6 rows selected.

SQL>

1 14-JUL-B3 СЙ75 14-JUL-83 BB

2 14-JUL-03 Gfi8 15-JUL-B3 Gfl

26 15-JUL-Q3 LB5 16-JUL-B3 СЙ

Рис. 5.23. Многострочный комментарий в SQL-сценарии

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

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

SUMФункция SUM суммирует значения и возвращает итог. Чтобы увидеть, как

она работает, введите следующий код:

SELECT * FROM plsql!01_purchase;SELECT SUM(quantity) FROM plsqll01_purchase;

162 Глава 5

COUNTФункция COUNT, как нетрудно догадаться, под считывает записи. Возмож-

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

SELECT COUNT(*) FROM plsqllOljpurchase;

А теперь, когда я показал вам эту команду, позвольте объяснить, почему еене следует использовать. Указывая "*" вместо имен столбцов, вы неявно застав-ляете Oracle считывать всю таблицу. Это не страшно для маленьких учебныхтаблиц, но представляет собой серьезную проблему в системах с сотнями тысяч,а тем более с миллиардами записей. Полное сканирование таблицы намного за-медляет выдачу результата и вынуждает Oracle отвлекать компьютерные ресурсыот главных задач, для выполнения которых предназначалась база данных, чтотормозит работу всей компании. Гораздо лучше указать какой-нибудь одинстолбец, по которому функция COUNT будет считать записи:

SELECT COUNT(product_name) FROM plsqll01_purchase;

Как правило, предпочтение отдается первому столбцу таблицы — по причи-нам, которые будут объяснены в следующей главе. Можно поступить еще про-ще, указав в качестве аргумента функции COUNT не имя столбца, алитеральное значение:

SELECT COUNT(1) FROM plsqll01_purchase;

Строго говоря, это заставляет Oracle возвращать значение "1" для каждой за-писи в таблице. С тем же успехом можно подставить в функцию фразу "Hi There";какое именно литеральное значение будет использовано — неважно, посколь-ку само это значение функция игнорирует. Она лишь подсчитывает записи исообщает, сколько их было найдено.

Функция COUNT имеет одно интересное свойство: если указать столбецтаблицы, записи которой подсчитываются, будут учтены только записи, содер-жащие в этом столбце какое-либо значение. Этим можно воспользоваться дляопределения процента записей, имеющих null-значение в определенномстолбце. После ввода показанного ниже кода обратите внимание, что перваякоманда выводит общее количество записей в таблице; вторая команда выдаеттот же результат, поскольку ни в одной из записей название товара не являетсяпустым; третья команда сообщает, сколько записей содержит заполненныйстолбец LAST_STOCK_DATE; последняя команда дает вам процент записей, вкоторых этот столбец заполнен. Информация такого рода может пригодиться,когда вы определяете полезность какого-либо столбца. После ввода командсравните результаты с показанными на рис. 5.24.

SELECT COUNT(1) FROM plsqll01_product;

SELECT COUNT(product_name) FROM plsql!01_product; '

SELECT COUNT(last_stock_date) FROM plsqll01_product;

SELECT COUNT(last_stock_date) / COUNT(product_name)"Populated Records"

FROM plsql!01_product;

Встроенные функции SQL 163

f, Oracle SQL-Plus

File Edit Search flptions HelpSQL> SELECT COUHT(1) FROM plsql101_product;

COUHT(1)

6

яви

SQL>

SQL> SELECT COUNT(product_nane) FROM plsql181_product;

COUNT (PRODUCTJWME)

6

SQL>SQL> SELECT COUNT(last_stock_date) FROH plsq!101_product;

CuUNT(LflST_STOCK_DflTE)_

SQL>SQL> SELECT COUNT(last stoch_date) / COUNT(product_naine) "Populated Records"

2 FROM plsql1B1_product;

Populated Records

.833333333

SQL>

Рис. 5.24. Использование функции COUNT

AVGФункция AVO возвращает среднее значение по указанному столбцу. Поско-

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

SELECT AVG(product_price) FROM plsql!01_product;

MINФункция MIN возвращает наименьшее из значений, содержащихся в ука-

занном столбце. Например, чтобы узнать, сколько стоит самый дешевый товариз таблицы PLSQL101_PRODUCT, можно ввести такую команду:

SELECT MIN(product_price) FROM plsql!01_product;

MAXКак вы наверняка догадались, функция МАХ возвращает наибольшее из

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

164 Глава 5

вара в таблице PLSQL101_PRODUCT, можно воспользоваться следующейкомандой:

SELECT MAX(product_price) FROM plsql!01_product;

Функция MAX применяется в целом ряде случаев. Например, в будущем пе-ред вами может встать вопрос о сокращении длины текстового столбца сущест-вующей таблицы. Вы захотите узнать, какая часть столбца действительноиспользуется, что потребует определения максимальной длины текста в этомстолбце. Это легко сделать, указав в качестве аргумента функции МАХ длинустолбца, как показано во второй из следующих команд:

fjgjgea DESC plsq!101_purchase;SELECT MAX(LENGTH(product_name)) FROM plsqll01_purchase;

Группирование данных с помощью конструкцииGROUP BY

Теперь, когда вы знаете о групповых функциях, пора научиться создаватьгруппы. Это делается путем добавления конструкции GROUP BY в операторSELECT, как показано в следующем примере:

SELECT * FROM plsql!01_purchase;

SELECT product_name, SUM(quantity)FROM plsqll01_purchase

GROUP BY product_name;

Введите этот код и сравните результаты с показанными на рис. 5.25. Как ви-дите, после конструкции GROUP BY просто указывается столбец, по значени-ям которого будет выполняться группирование. Обычно это первый столбец всписке SELECT.

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

SELECT SUBSTR(product_name, 1, 15) "Product",SUM(quantity) "Total Sold",

AVG(quantity) "Average",

COUNT(quantity) "Transactions",MIN(quantity) "Fewest",

MAX(quantity) "Most"

FROM plsqllGlJpurchaseGROUP BY product_name;

После ввода этой команды сравните полученные результаты с рис. 5.26.

Встроенные функции SQL 165

* Oiacle SQL'Plus

File E dit Search Options Help

SQL> SELECT « FROM plsq!1B1_purchase;

MI-ТЕЗ

PRODUCT NAME QUANTITV PURCHASE_ SAL

Small Widget

Medium Uodget

Chrome Phoobar

Small Widget

Medium Uodget

Round Snaphoo

б rows selected.

1 1J»-JUL-03 С А

75 Ut-JUL-03 BB

2 U»-JUL-03 GA

8 1S-JUL-B3 GA

20 15-JUL-83 LB

5 16-JUL-03 CA

SQL>

SQL> SELECT product_nane, SUM(quantity)

2 FROM plsqltei_purchase

3 GROUP BV product_nane;

PRODUCT NftME SUM(QUANTITV)

Chrome Phoobar

Medium Uodget

Round Snaphoo

Small Widget

SQL>

29559

Рис. 5.25. Использование конструкции GROUP BY

lA-Oracle SQL-Plus' ;>л>-9?>11->: •-••.,•„-.•. •$•{••.; .,;;: • r.~. .-.:>,-.^ •- '.• , • : ' • - . - • -

fie Edit Search Options Help

SQL> SELECT SUBSTR(product name. 1. 15) "Product".2 SUM(quantity) "Total Sold",3 fiUG (quantity) "Average",H COUNT(quantity) "Transactions",5 HI N( quantity) "Fewest",6 MflX( quantity) "Most"7 FROM plsql101 purchase8 GROUP BV pruduct_nane;

Product Total Sold Average Transactions Fewest

Chrome Phoobar 2 2 1 2Medium Uodget 95 47.5 2 2 вRound Snaphoo 5 5 1 5Small Widget 9 4.5 2 1

SQL>

AJ

• • • • ' • • < esc

4J

.

Most

275

58

AРис. 5.26. Использование нескольких групповых функций в одном

операторе SELECT

166 Глава 5

Включение и исключение группс помощью конструкции HAVING

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

Однако после создания групп возникает новая задача: фильтрация самихгрупп на основе групповой информации. Предположим, что вашей компаниине хватает складских площадей и она намерена сократить запас товаров наскладе. Для поддержки этого мероприятия вам требуется составить списокплохо продающихся товаров — например, тех, для которых общий объем про-даж составляет менее пяти штук. Здесь и пригодится конструкция HAVING.Она фильтрует группы на основе групповых значений. В отличие от конструк-ции WHERE, фильтрующей записи до группирования, конструкцияHAVING фильтрует уже сформированные группы. Чтобы увидеть, как этопроисходит, введите следующий код (он идентичен предыдущему, за исклю-чением единственной строки в конце, поэтому вы можете сэкономить время,воспользовавшись командой EDIT и просто добавив строку с конструкциейHAVING):

-I у .: SELECT SUBSTR(product_name, 1, 15) "Product",

SUM(quantity) "Total Sold",

AVG(quantity) "Average",

COUNT(quantity) "Transactions",

MIN(quantity) "Fewest",

MAX (quantity) "Most".

FROM plsql!01_purchaseGROUP BY product_name;

HAVING SUM(quantity) < 5;

Как видите, оператор SELECT включает в выходные данные только плохопродающиеся товары. А если вам нужно решить обратную задачу — составитьсписок хорошо продающихся товаров? Просто измените условие в конструк-ции HAVING, как показано ниже:

SELECT SUBSTR(product_name, I, 15) "Product",

SUM(quantity) "Total Sold",AVG(quantity) "Average",

COUNT(quantity) "Transactions",

MIN(quantity) "Fewest",

MAX(quantity) "Most"

FROM plsql!01_purchaseGROUP BY product_name;

HAVING SUM(quantity) >= 5;

ИтогиЭта глава содержала много базового материала. Вы начали с изучения одно-

строчных SQL-функций, к которым относятся системные переменные

Встроенные функции SQL 167

(SYSDATE, USER и USERENV), числовые функции (ROUND и TRUNC),текстовые функции (UPPER, LOWER, INITCAP, LENGTH, SUBSTR, INSTR,LTRIM и RTRIM), функции для работы с датами (ADD_MONTHS,LAST_DAY и MONTH_BETWEEN), функции преобразования данных(TO_CHAR и TO_DATE), а также полезные, но плохо поддающиеся классифи-кации функции DECODE и NVL. Затем вы познакомились с групповыми фун-кциями (SUM, COUNT, AVG, MIN и MAX) и научились подводитьпромежуточные итоги с помощью конструкции GROUP BY, включаемой воператор SELECT. В заключение вы научились исключать из выходных данныхцелые группы промежуточных итогов, помещая в оператор SELECT конструк-цию HAVING.

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

Вопросы1. На какой строке будет выдано сообщение об ошибке при выполнении

этой команды?

INSERT INTO plsql!01_product (product_name,product_price,quantity_on_hand,last_stock_date )

VALUES ('New Product',1.95,10,TO_CHAR(USER) )

''

A. 1

B.2

C. 7

, D. 10

E. Команда будет выполнена успешно

2. Какое из перечисленных утверждений истинно?

A. ROUND(4.5, 0) < TRUNC(4.5,0)

B. ROUND(4.1, 0) < TRUNC(4.2, 0)

C. ROUND(8.9, 0) > TRUNC(8.9,0)

D. ROUND(8.9, 1) > TRUNC(8.95, 2)

168 Глава 5

3. Какое значение вернет приведенная ниже функция DECOt)E?

DECODE('B','A', 'One','Е1, 'Five',Т, 'Nine','О1, 'Fifteen','U1, 'Twenty-one','N/A'

)A. One

B. Two

C. Five

D. N/A

4. Какое значение вернет приведенная ниже функция SUBSTR, когдавстретит значение'Psychic trance;, Medium's столбце ITEM_DESC?

SUBSTR(item_desc,INSTR(item_desc,

. i.t i»

1)+2,

99)

A. Medium >

B. Psychic trance

C. Psychic trance, Medium

5. Какая из перечисленных функций вернет последний день 2002 года?

A. SELECT ADD_MONTHS(LAST_DAY(14-OCT-02'), 1)FROM DUAL;

B. SELECT ADD_MONTHS(LAST_DAYC15-OCT-02'), -1)FROM DUAL;

C. SELECT ADD_MONTHS(LAST_DAY(16-OCT-02'), 2)FROM DUAL;

D. SELECT ADD_MONTHS(LAST_DAY(17-OCT-02'),-2)FROM DUAL;

Ответы на вопросы1.D. 10

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

Встроенные функции SQL 169

2. С. ROUND(8.9, 0) > TRUNC(8.9, 0)

Объяснение Вариант А сводится к 5 < 4, что ложно. Вариант В сво-дится к 4 < 4, что также ложно. Вариант D сводится к 8.9 > 8.95, что...в общем, вы догадались. Вариант С сводится к 9 > 8, что истинно.

3. D. N/A

Объяснение Поскольку входное значение СВ') является текстом,оно должно точно совпадать с одной из опций DECODE; иначе будетвозвращено значение "else" в конце функции. В данном случае точногосовпадения нет, поэтому возвращается значение'N/A'.

4. A. Medium

Объяснение Просмотрите раздел "Текстовые функции", чтобыосвежить в памяти эту тему.

5. С.

Объяснение Поскольку вы имеете дело с функцией LAST_DAY,можно игнорировать день месяца — все четыре функции возвращают31 октября 2002 года. Добавив два месяца к этой дате, как это делается вфункции С, вы получите 31 декабря 2002 года.

• ,• .... •

Глава

Индексы и ограничения

172 Тдаваб

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

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

DROP TABLE plsql!01_person;CREATE TABLE plsqll01_person (

person_code VARCHAR2(3),first_name VARCHAR2(15),last_name VARCHAR2(20),hiredate DATE

INSERT INTO plsql!01_person VALUESCCA', 'Charlene', 'Atlas', 'Ol-FEB-02');

INSERT INTO plsql!01_person VALUES('GA', 'Gary', 'Anderson', '15-FEB-02');

INSERT INTO plsql!01_person VALUESCBB', 'Bobby", 'Barkenhagen', '28-FEB-02');

INSERT INTO plsql!01_person VALUESCLB', 'Laren', 'Baxter', 'Ol-MAR-02');

DROP TABLE plsql!01_product;CREATE TABLE plsql!01_product (

product_name VARCHAR2(25),product_price NUMBER(4,2),quantity_on_hand NUMBER(5,0),laststockdate DATE

INSERT INTO plsql!01_product VALUES('Small Widget', 99, 1, '15-JAN-03');

INSERT INTO plsqllOl product VALUES('Medium Wodget', 75, 1000, 45-JAN-02');

INSERT INTO plsql!01_product VALUES('Chrome Phoobar

1, 50, 100, '15-JAN-03');

INSERT INTO plsql!01_product VALUES('Round Chrome Snaphoo', 25, 10000, null);

INSERT INTO plsql!01_product VALUES('Extra Huge Mega Phoobar +', 9.95, 1234, ' 15-JAN-04'.) ;

INSERT INTO plsql!01_product VALUES ('Square Zinculator',45, 1, TO_DATE('December 31, 2002, 11:30 P.M.',

Индексы и ограничения 173

'Month dd, YYYY, HH:MI P.M.')

DROP TABLE plsql!01_purchase;CREATE TABLE plsqll01_purchase (

product_name VARCHAR2(25),salesperson VARCHAR2(3),purchase_date DATE,

. quantity NUMBER(4,2)

INSERT INTO plsql!01_purchase VALUES('Small Widget

1, 'CA', 44-JUL-03', 1);

INSERT INTO plsqllOl purchase VALUES('Medium Wodget', 'BB', '14-JUL-03', 75);

INSERT INTO plsql!01_purchase VALUES('Chrome Phoobar', 'GA', '14-JUL-03

1, 2);

INSERT INTO plsql!01_purchase VALUES('Small Widget', 'GA', '15-JUL-03', 8);

INSERT INTO plsql!01_purchase VALUES('Medium Wodget', 'LB', '15-JUL-03',.20);

INSERT INTO plsql!01_purchase VALUES('Round Snaphoo', 'CA', 46-JUL-03', 5);

DROP TABLE plsql!01_old_item;CREATE TABLE plsql!01_old_item (

item id CHAR(20),

itenTdesc CHAR(25)

INSERT INTO plsql!01_old item VALUES('LA-101

1, 'Can, Small');

INSERT INTO plsql!01_old_item VALUES('LA-102', 'Can, Large');

INSERT INTO plsq!101_old_item VALUES('LA-ЮЗ', 'Bottle, Small');

INSERT INTO plsql!01_old_item VALUES('LA-104', 'Bottle, Large');

INSERT INTO plsqllOl old item VALUES('NY-101', 'Box, Small');

INSERT INTO plsq!101_old_item VALUES('NY-102', 'Box, Large');

INSERT INTO plsq!101_old_item VALUES('NY-ЮЗ', 'Shipping Carton, Small');

INSERT INTO plsq!101_old_item VALUES

('NY-104', 'Shipping Carton, Large1);

. •• • <

ИндексыНесомненно, вы знакомы с индексными указателями (индексами), разме-

щаемыми на последних страницах книг.

174 , Глава 6

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

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

Когда таблица содержит большое число записей, ее полный просмотр с цельюпоиска определенных записей может занять у Oracle (как и у любой другойСУБД) много времени — сравните это с перелистыванием всей книги в поискестраниц, где обсуждается определенная тема. Oracle позволяет создавать вто-рую, скрытую таблицу, содержащую один или несколько важных столбцов изглавной таблицы вместе с указателями на строки основной таблицы. В данномслучае вместо номеров страниц указателями являются номера строк. Далее ябуду называть эту скрытую таблицу индексом. Просматривая индекс, Oracleможет узнать, какие строки содержат запрашиваемые данные (при условии,что эти данные находятся в столбцах, составляющих индекс). Поскольку ин-декс намного меньше таблицы, на которую он ссылается (подобно тому каккнижный индекс намного меньше полного текста книги), поиск данных в таб-лице с индексом будет выполняться, существенно быстрее, чем в таблице безиндекса. Например, одновременно с написанием этого текста я запускал небо-льшую SQL-процедуру, которая вставляла миллион строк в тестовую таблицу.(Отом, как создавать подобные процедуры, рассказывается в главе 8.) Выбор изэтой таблицы записей с определенным значением занял 18.9 секунды. Послесоздания индекса тот же самый запрос стал выполняться всего лишь за 0.6 се-кунды. Добавление индекса позволило получать ответы в 31 раз быстрее!

На рис. 6.1 показано, как выглядел бы стандартный индекс для таблицыPLSQL101_PERSON. В данном случае таблица проиндексирована по столбцуPERSON_CODE. Обратите внимание, что индекс отсортирован поPERSON_CODE, хотя таблица хранит записи в последовательности их ввода.Записи индекса всегда сортируются по тем столбцам, которые он содержит.Столбец ROWNUM, входящий в индекс, отслеживает положение каждой стро-ки в исходной таблице.

ТаблицаPLSQL101 PERSON

PERSON.CODE

СА

GA

ВВ

F1RST.NAME

Chariene

Gary

Bobby

Laren

LASTNAME

Adas

Anderson

Barkenhagen

Baxter

HIRE_DATE

'01-Feb-02

15-Feb-02

01-Feb-02

01-Feb-02

ИндексPERSON CODE

PERSON.CODE

BB

CA

GA

LB

ROWNUM

3

1

2

4

Рис. 6.1. Таблица с одним индексом

Индексы и ограничения 175

На рис. 6.2 показана аналогичная связь между таблицей PLSQL101_PURCHASE и двумя индексами. Здесь нет ошибки, таблица действительно мо-жет иметь более одного индекса. Но зачем это нужно? Таблица PLSQL101_PURCHASE служит хорошей иллюстрацией к ответу. Она содержит как мини-мум два столбца, являющихся потенциальными кандидатами на индексирова-ние: столбец товаров и столбец продавцов. Весьма вероятно, что запросы ктаблице часто будут основываться на названии товара, и не менее вероятно, чтодругие запросы будут основываться на имени продавца. Создавая отдельныйиндекс для каждого из этих столбцов, вы получаете такое же увеличение скоро-сти поиска, как и в случае таблицы с одним индексом, но теперь оно будетиметь место как при поиске по названию товара, так и при поиске по именипродавца.

После того как вы создали индекс, Oracle автоматически поддерживает егосинхронизацию с таблицей. Любые операции INSERT, UPDATE или DELETEнад этой таблицей будут автоматически изменять ее индекс, и любая командаSELECT будет выполняться с привлечением индекса, если он содержит требуе-мые столбцы. Добавление или удаление индексов не влияет на операции с таб-лицей — любая использовавшая ее программа по-прежнему будет работать,хотя и более медленно. В случае удаления таблицы все ассоциированные с нейиндексы также будут удалены, поскольку без таблицы индекс бесполезен.

ТаблицаPLSQL101 PURCHASE •

ИндексPRODUCT NAME

ИндексSALESPERSON

PRODUCT.NAME

Small Widget

Medium Wodget

Chrome Phoobar

Small Widget

Medium Wodget

Round Snaphoo

QUANTITY

1

75

2

8

20

5

PURCHASEJWE

H-Jul-03

14-Jul-03

14-Jul-03

14-Jul-03

14-JuMB

14-Jul-03

SALESPERSON

CA

BB

GA

GA

IB

CA

PRODUCT.NAME

Chrome Phoobar

Medium Wodget

Medium Wodget

Round Snaphoo

Small Widget

Small Widget

ROWNUM

3

2

5

6

1

4

SALESPERSON-

BB

CA

CA

GA

GA

LB

ROWNUM

3

2

5

6

1

4

Рис. 6.2. Таблица с индексами по двум разным столбцам

7 Зак. 725

176 Глава 6

Команда удаления индекса имеет следующий синтаксис:

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

Когда индексы помогают?Индексы уменьшают время отклика команд, выполнение которых требует

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

Добавление индексов к таблице не ускоряет ввод данных командамиINSERT; фактически это дает обратный эффект. Почему присутствие индексазамедляет вставку? Вспомните, что индекс тоже является таблицей. Когда выдобавляете запись к проиндексированной таблице, Oracle должен добавить за-пись и к самой таблице, и к ее индексу, т.е. выполнить две вставки для каждойзаписи. В результате добавление индекса к таблице вызовет более чем двукрат-ное увеличение времени вставки (собственно две вставки плюс небольшое до-полнительное время на их координацию). Добавление двух индексов замедлитоперацию вставки в три раза, трех индексов — в четыре раза, и т.д.

Таким образом, использование индексов — это компромисс. Они замедля-ют ввод данных, но ускоряют их считывание. Следовательно, индексированиетаблиц нежелательно в тех приложениях, где ввод данных должен происходитькак можно быстрее. Например, в системе, обслуживающей сеть розничных ма-газинов, от кассовых терминалов требуется максимальная скорость выполне-ния транзакций. В этом случае индексирование таблицы транзакций будетошибкой, поскольку приведет к замедлению ввода данных. С другой стороны,в той же самой компании могут работать специалисты, которым нужно выпол-нять запросы, анализирующие транзакции, а эти запросы сильно выиграют отналичия правильно индексированных таблиц. Как удовлетворить этим проти-воречивым требованиям? Во многих системах данные о транзакциях каждуюночь автоматически копируются из рабочих таблиц во вспомогательные индек-сированные таблицы, которые на следующий день используются для анализа.Вставка данных в индексированную таблицу занимает намного больше време-ни, чем вставка в таблицу транзакций, не имеющую индексов, но это никого неволнует, поскольку магазины закрыты, покупателей нет, а вся работа выполня-ется компьютерами.

Чем больше таблица, тем больший выигрыш можно получить от ее индекси-рования. Например, все созданные в предыдущих главах таблицы настолькомалы, что любые операции с ними выполняются практически мгновенно. Какследствие, индексирование этих таблиц не даст заметной выгоды. По мере рос-та таблиц выигрыш увеличивается. В качестве примера снова возьмем таблицус миллионом записей. В таблице 6.1 показано, сколько времени занимали раз-личные операции DML при наличии и отсутствии индекса. Первая строка со-держит статистику по оператору SELECT, которая приводилась выше, тогдакак последующие строки демонстрируют влияние индекса на командыUPDATE и DELETE. Время измерено в секундах. Посмотрите, насколько бы-стрее выполняются операции, и представьте, каким героем вы могли бы стать,придя на новую работу и в первый же день сократив время обработки данных счасов до минут.

Индексы и ограничения 177

Таблица 6.1. Время выполнения операций DML при наличиии отсутствии индекса

Операция

SELECT(50 записей)

UPDATE(50 записей)

DELETE(50 записей)

Без индекса

18.9

19.7

19.6

С индексом

0.6

0.5

0.06

Увеличение скоро-сти

в 31. 5 раза

в 39.4 раза

в 326.7 раза

Как создаются индексыСоздать индекс очень легко . Для этого используется команда со следующим

синтаксисом:' • ' - . ' •• -' - •' ' . - ' . < : . - - . ,-' ' '

CREATE INDEX имя_индекса ON имя_таблицы(имя_столбца);

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

CREATE INDEX имя_индекса ON имя_таблицы (имя_первого_столбца,имя_второго_столбца

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

CREATE INDEX plsqll01_person_code_index

ON plsql!01_person (person_code) ; i

Этот индекс наиболее полезен при выполнении поиска со ссылкой наPERSON_CODE в конструкции WHERE. Если же поиск в большинстве случа-ев ведется по имени и фамилии продавца, то более подходящим окажется ин-декс по этим'двум столбцам. Такой индекс создается следующей командой:

CREATE INDEX plsqll01_person_name_index

ON plsqll01__person (last_name, first_name) ;

Это пример составного индекса (composite index), иногда называемого сцеп-ленным индексом (concatenated index). Составной индекс — это просто индекспо двум и более столбцам таблицы. Он подходит в любом случае, когда некото-рые столбцы таблицы с большой вероятностью будут использоваться в конст-рукции WHERE совместно — например, имя и фамилия, или город, штат ипочтовый индекс. Чтобы Oracle использовал составной индекс, в конструкцию

178 Глава 6

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

ПримечаниеМаксимальное число столбцов, которые можно включатьв стандартный индекс Oracle, равно 32.

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

Различные типы индексовВероятно, вы уже поняли, что индексирование — это, по существу, способ

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

Индексы В*-дереваТип индекса, используемый в Oracle по умолчанию, называется В*-деревом

(В*-Тгее), Организация записей в индексе В*-дерева показана на рис. 6.3.При создании индекса В*-дерева Oracle анализирует значения в индексиру-

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

Примечание^ "*• В примере на рис. 6.3 блоки-ветви делят алфавит на равные части.

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

Достоинство индекса В*-дерева в том, что он позволяет Oracle быстро иден-тифицировать записи, не считывая их. За счет минимизации объема считывае-мых данных, а следовательно, и объема выполняемой работы, Oracle можетвозвращать результаты гораздо быстрее. (Помните, что улучшения, перечис-ленные в таблице 6.1, были достигнуты при использовании стандартного ин-декса В*-дерева.) Предположим, например, что таблица содержит миллиардзаписей, и вы хотите просмотреть запись с определенным идентификацион-ным номером. Таблица не обязательно будет отсортирована по идентификаци-

Индексы и ограничения 179

Таблица базы данных

ROWNUM

1

2

3

4

5

6

7

8

UST.NAME

Norton

Gutwirth

Trumble

Fletcher

Zoraster

Moss

Allen

Smith

Блоки-ветви <

Блоки-листьяс номерамистрок

Allen-7Retcher-4

Gutwirth • 2Moss - 6

Norton - 1Smith -8

Trumble - 3Zoraster -5

Рис. 6.З. Организация записей в индексе В*-дерева

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

Поскольку сила индекса В*-дерева заключена в делении данных на множе-ства и подмножества, этот тип индекса оптимален в тех случаях, когда индекси-руемый столбец содержит широкий диапазон значений — скажем, имена илидаты. Для столбца с узким диапазоном значений (например, "пол") лучше всегоиспользовать битовый индекс.

Битовые индексыКоль скоро структура индекса В*-дерева оптимальна для индексирования

столбца со многими уникальными значениями, логично предположить, чтодля столбца с малым числом уникальных значений может лучше подойти дру-гая структура индекса. Например, столбец "пол", скорее всего, будет содержатьодно из трех значений: "М" ("мужской"), "F" ("женский") или "U" ("Unknown" —

180 Глава 6

Таблица 6.2.

Шаг

12

3

4

5

67

8. , ' • - - - •

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

ЗР

31

Количество шагов, требуемых для поискаопределенного значения в индексе В*-дерева

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

1000000000

500000000

250000000

125000000

62500000

31250000

15625000

7812500

3906250

1 953 125

976563

488 281

244141

122070

61035

30518

15259

7629 .

3815

1907

954

477 '

238

119

60

30

15

7

4

2

1

Индексы и ограничения 181

"неизвестный"). Помещение столь малого количества уникальных значений вструктуру В*-дерева не имеет смысла, поскольку подход "делить на подгруппышаг за шагом", делающий В*-дерево столь удобным для поиска многовариантныхзначений, не даст большого выигрыша. В такой ситуации лучше использовать би-товый индекс. Его устройство в несколько упрощенном виде показано на рис. 6.4.

ПримечаниеПод кардинальностью (cardinality) понимается количество уникальныхзначений, которые может содержать столбец. Столбец с малымколичеством уникальных значений (например, пол или значениятипа "истина "/"ложь ") имеет низкую кардинальность. Столбецс большим количеством уникальных значений (например, цены илиимена) имеет высокую кардинальность.

Для тех запросов SELECT, в которых конструкция WHERE содержит столбецс низкой кардинальностью, предварительное создание битового индекса можетзначительно сократить время получения ответов. Рост скорости обусловлен дву-мя факторами: во-первых, битовые индексы могут быть довольно компактными(поскольку для хранения бит требуется лишь малая часть того места, которое от-водится другим типам данных в стандартном индексе), а во-вторых, значения" 1"или "О" могут анализироваться компьютером очень быстро.

Команда создания битового индекса практически идентична команде со-здания стандартного индекса; добавляется только слово "BITMAP", как пока-зано в следующем примере:

CREATE BITMAP INDEXимя_индекса ON имя_таблицы(имя_спголбца)',

Обеспечение целостности данных:ограничения

Одной из самых важных задач, стоящих при разработке таблиц, являетсяобеспечение наивысшего возможного качества данных, которые вводятся поль-зователями в эти таблицы. "Грязные" данные содержат неверные коды, слиш-

Таблицабазы данных

_ROWNUM_

1

2

3

4

5

6

7

8

IAST:NAME;

Norton

Gutwirth

Trumble

Fletcher

Zoraster

Moss

Allen

Smith

SENDS*

F

M

M

M

F

M

F

U

Битовый индекспо столбцу "пол"

ROWNUM

1

2

3

4

5

6

7

8

FEMALE;1

1

1

MALE

1

1

1

1

UNKNOWN' "'••

1

Рис. 6Л. Организация записей в битовом индексе

182 Глава б

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

Что такое ограничение?Ограничение (constraint) — это одно и более условий, которым должна удов-

летворять введенная пользователем запись, чтобы Oracle вставил ее в таблицу.Ограничения хранятся как часть определения таблицы и после создания при-меняются автоматически. .Когда введенная кем-либо команда INSERT илиUPDATE нарушает ограничение, Oracle прерывает ее выполнение, производитоткат и выдает сообщение об ошибке.

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

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

. . . ,: . ;

NOT NULLС одним из типов ограничений вы уже познакомились в главе 2, когда указыва-

ли в команде CREATE TABLE, что столбцы не должны содержать null-значений.Чтобы не заставлять вас перелистывать страницы, приведу эту команду еще раз:

CREATE TABLE plsql!01_purchase (product_name VARCHAR2(25) NOT NULL,product_price NUMBER(4,2} NOT NULL,purchase_date DATE

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

ALTER TABLE имя_таблицы MODIFY (имя_столбца NOT NULL);

Примените ее к одной из ваших таблиц. Таблица PLSQL101_PERSON со-держит столбцы FIRST_NAME и LAST_NAME; логично предположить, чтобез имени и фамилии информация о человеке будет неполной. Чтобы сделатьэти столбцы обязательными, введите следующие команды и сравните результа-ты с показанными на рис. 6.5:

ALTER TABLE plsql!01_person MODIFY (first_name NOT NULL);ALTER TABLE plsql!01_person MODIFY (last_name NOT NULL);

Теперь протестируйте ограничение с помощью приведенного ниже кода.Сравните полученные результаты с теми, что показаны на рис. 6.6.

INSERT INTO plsql!01_person VALUES ('XL

1, 'Xaviera

1, NULL, 45-NOV-03'

. • '

Индексы и ограничения 183

A Oracle SQL'Plus

Be Edit Search flptions Help

SQL> flLTER TflBLE plsqll81_person MODIFY (first_nane HOT HULL);

Table altered.

SQL> BLTER TflBLE plsq!101_person MODIFV (last_name NOT NULL);

Table altered.

SQL>

ИЕЕЗ

Рис. 6.5. Изменение существующей таблицы для присваиванияее столбцам статуса обязательных

A Oiacle SQL'Plus

File Edit 2each Qpfons Help

SQL> INSERT INTO plsqllB1_person UflLUES (2 ' X L 1 , 'Xauiera', NULL, '15-NOU-831

3 )* ;

INSERT INTO plsqll01_person UflLUES (

*

ERROR at line 1:

ОНв-в11*Ов: cannot insert NULL into (l>PLSQL1fl1"."PLSQL101_PERSON".'fLflST_Hfll1E")

SQL>

Рис. 6.6. Проверка ограничения NOT NULL

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

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

PLSQL101_PERSON всегда будут содержать имя и фамилию. Однако пользе-

184 Глава 6

вателям ничто не мешает ввести данные об одном человеке дважды. Вы думае-те, им лучше знать? В идеале это так. Но люди отвлекаются, забивают, что ужесделано, а иногда просто бывают невнимательными; жизнь такова, что все мыделаем ошибки. Помните правило:

Если что-то может быть сделано неправильно, „оно БУДЕТ сделано неправильно.

Это не означает, что отдельно взятый человек совершит все возможныеошибки. Но за время существования базы данных ее будут использовать мно-гие люди, поэтому вполне вероятно, что в пределах этой группы каждая из воз-можных ошибок рано или поздно будет сделана. Одна из составляющих вашейработы — думать наперед и принимать меры к тому, чтобы ненамеренные по-пытки ввести "дефектные" данные не увенчались успехом.

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

Имея это в виду, посмотрим, какой синтаксис используется для созданияограничения, гарантирующего уникальность значений:

ALTER TABLE имя_таблицыADD CONSTRAINT имя_огрантения UNIQUE(имена_столбцов) ;

Применив этот синтаксис к рассматриваемой ситуации, мы получим приве-денный ниже код. Введите его и сравните результаты с показанными нарис. 6.7.

ALTER TABLE plsqllOljperson

ADD . CONSTANT. plsqllQl_person_un;Lque .UNIQUE ,(first_name,last_name,

hiredate '

Теперь проверьте новое ограничение с помощью приведенных нижекоманд, результат выполнения которых показан на рис. 6.8.

INSERT INTO plsqll01_person VALUES' (•'

'LN', 'Linda', 'Norton', ' Ol-JUN-03 ' ) ;

INSERT INTO plsqll01_person VALUES (< ;

'NL', 'Linda', 'Norton1, ' Ol-JUN-03 ' ) ;

Индексы и ограничения 185

Oracle SOL'Plu

£* Search Options HelpSQL> ALTER TABLE plsq!101_person

2 ADD CONSTRAINT plsql101_person_unlque UNIQUE (

НИИ

345б7

first_nane,last name.hire~date

Table altered.

SQL>

Рис. 6.7. Создание ограничения уникальности

Ж Oiitcle SQL-Plus ИНЕЗ£Je £dit Search Qptions Ц*

SQL> INSERT INTO plsqll01_person UALUES (2 ' L N ' , 'Linda1, •Norton*. •B1-JUN-B31);

i '

1 row created.

SQL>SQL> INSERT INTO plsqll81 person UALUES (

2 -NL1. -Linda', TNorton', •81-JUN-B3>);INSERT INTO plsqll01_person UALUES (

*ERROR at line 1:ORA-oeOBI: unique constraint (PLSQL1B1.PLSQL101_PERSON_UNIQUE) uiolated

SQL>

Рис. 6.8. Проверка ограничения уникальности

Как видите, вторая команда привела к ошибке, В ней указан другой личныйкод, но имя, фамилия и дата приема на работу оставлены такими же, как и впервой команде, поэтому вставка записи была запрещена.

Заметьте также, что в сообщение об ошибке включается имя ограничения,не позволившего вставить запись (PLSQL101_PERSON_UNIQUE). Учиты-вая это, желательно присваивать ограничениям максимально информатив-ные имена (в пределах 30 символов, допускаемых для имени). Я рекомендуюначинать имя ограничения с имени таблицы, а затем указывать назначениеограничения.

186 Глава 6

Когда вы создаете ограничение уникальности, Oracle неявно создает уникаль-ный индекс (unique index). Этот индекс содержит столбцы, указанные в командеALTER TABLE...ADD CONSTRAINT, а его имя совпадает с именем ограниче-ния. Уникальный индекс помогает Oracle идентифицировать повторяющиесязаписи. Создавая этот индекс, Oracle считывает все существующие записи таб-лицы и представляет их в индексе. По этой причине создание ограничения уни-кальности будет успешным только в том случае, если оно не нарушаетсятекущими записями таблицы. Если таблица содержит записи с повторяющи-мися значениями в указанных вами столбцах, то в ответ на команду ALTERTABLE...ADD CONSTRAINT будет выдано сообщение об ошибке с объясне-нием проблемы.

CHECKКонтрольное ограничение (check constraint) позволяет определить, каким

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

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

ALTER TABLE имя_таблицы ADDCONSTRAINT [имя_ограничения]СНЕСК1имя_столбца условие)

»

Параметр имя_таблицы, разумеется, идентифицирует таблицу, столбцы ко-торой должны контролироваться. Необязательный параметр имя_ограниче-ния позволяет вам самостоятельно присвоить имя ограничению. Если опуститьэтот параметр, Oracle выберет имя автоматически, причем оно не будет нестикакой-либо полезной информации. Лучше присваивать имя вручную, чтобыпри нарушении ограничения в сообщении об ошибке стояло именно заданноевами имя — предположительно, более информативное, чем присвоенное поумолчанию.

Параметр имя_столбца идентифицирует столбец, к которому относитсяограничение. Параметр условие определяет, каким требованиям должны удов-летворять данные, чтобы попытка их вставки в таблицу была успешной. Всевместе эти параметры выглядят точно так же, как и условие в конструкцииWHERE оператора SELECT.

Теперь вам пора создать свое собственное ограничение. Первое упражнениепоможет поддерживать чистоту данных в таблице PLSQL101_PURCHASE пу-тем контроля за датами покупок. (Здесь мы определяем первую "разумную"дату как 30 июля 2000 года. В реальности определение такой даты зависит отприложения.) В этом упражнении следует отметить несколько моментов:

• Контрольное ограничение проверяет два различных условия, преждечем разрешить вставку данных. Эти условия соединены ключевымсловом AND.

Индексы и ограничения 187

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

• Во втором условии имя столбца подставлено в функцию TO_CHAR.Это обусловлено недоработкой (или, скажем так, особенностью)текущей версии Oracle, приводящей к выдаче сообщения об ошибкепри попытке сослаться в контрольном ограничении непосредственнона столбец с датами. Чтобы обойти эту "особенность", необходимопреобразовать дату в текст и проверять ее в этом формате. Однако присравнении текстовых строк февраль ("РЕВ") будет идти раньше января("JAN"), поскольку в алфавите "F" стоит перед "Г. Решение состоит втом, чтобы отформатировать столбец и дату в ограничении как числа,поставив на первое место год, затем месяц (в числовом виде) и вконце — день.

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

* Oiacle SQL-Plus

File Edit Search Options Help

SQL> ftLTER TftBLE plsql1B1_purchase ROD (23Ч5678

CONSTRAINT reasonable_date CHECK(

purchase_date IS NOT NOLLAND

TO_CHflR(purchase_date, •VYVV-MM-DD1) >- '20вв-06-ЗВ'

Table altered.

SQL>SQL> INSERT INTO plsql1B1 purchase UALOES (

2 'Small Midget1. 10, '28-FEB-OB', 'GO');

INSERT INTO plsql101_purchase UALOES (*

ERROR at line 1:ORft-02290: check constraint (PLSQL101.REflSONflBLEJJflTE) violated

SQL>

JJJ

Рис. 6.9. Использование контрольного ограничения на дату

ALTER TABLE plsql!01_purchase ADD (CONSTRAINT reasonable_date CHECK(

purchase_date IS NOT NULL

188 Глава 6

ANDTO_CHAR(purchase_date, ' YYYY-MM-DD' ) >= '2000-06-30'

INSERT INTO plsql!01_purchase VALUES ('Small Widget', 'GA', '28-FEB-OO' 10);

В некоторых случаях желательно установить на столбец контрольное огра-ничение, но разрешить null-значения; иначе говоря, столбец может быть пус-тым, но если он содержит значение, то оно должно удовлетворять одному илинескольким условиям. По умолчанию Oracle интерпретирует контрольныеограничения именно так. Пока вы не укажете конструкцию IS NOT NULL,null-значения будут приниматься. Чтобы увидеть, как это происходит, введитеследующий код и сравните результаты с показанными на рис. 6.10.

.+• Oracle SQL-Plus

£ie Ed» 'Search- flptioris Help r: ' ' ,: ' • , " д ' :

SQL> ALTER TABLE plsqlioi_product ADD (2 CONSTRAINT reasonable stock_date CHECK(3 TO_CHAR(last_stock_date, ' VVYY-MM-DD ' ) >- '2801-12-31'~

ИГ-Ш

)

Table altered.

SQL>SQL> INSERT INTO plsql101_product UALUES (

2 'Anodized Franifier1. 49, 5, NULL)

3 ;

1 row created.

SQL>

SQL> INSERT INTO plsql101 product UALUES (

2 'Spring-Loaded Pit Puller1, 49, 5, '30-DEC-01')

3 ;'Spring-Loaded Pit Puller', 49, 5, '3B-DEC-ei')

«

ERROR at line 2:

ORft-82298: check constraint (PLSQL1D1.REASONABLE_STOCK_DATE) violated

SQL>SQL>

Рис. 6.10. Добавление контрольного ограничения, разрешающего вводnull-значений

Индексы и ограничения 189

ALTER TABLE plsql!01_product ADD {

CONSTRAINT reasonable_stock_date CHECK(TO_CHAR(last_stock_date, 'YYYY-MM-DD') >= '2001-12-31'

)

)

INSERT INTO plsql!01_product VALUES (

'Anodized Framifier', 49, 5, NULL)

/

INSERT INTO plsq!101_product VALUES {

'Spring-Loaded Pit Puller', 49, 5, '30-DEC-01')

f

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

CREATE TABLE plsql!01_person (

person_code VARCHAR2(3) NULL,

first_name VARCHAR2(15) NOT NULL,

last_name VARCHAR2(20) NOT NULL,

hire_date DATE NULL

CONSTRAINT reasonable_liire_date CHECK (

TO_CHAR(hire_date, 'YYYY-MM-DD') >= 4930-01-01'

)

)

CREATE TABLE plsql!01_product (

product_name VARCHAR2(25) NULL,

product_price NUMBER(4,2) NULL

CONSTRAINT validjorice CHECK "(

product_price BETWEEN 0 AND 10000

),quantity_on_hand NUMBER(5) NULL

CONSTRAINT positive_quantity CHECK (

quantity_on_hand >= 0

),last_stock_date DATE NULL

CONSTRAINT reasonable_stock_date CHECK (

TO_CHAR(last_stock_date, 'YYYY-MM-DD1)

>= '2001-12-31'

190 Глава 6

Наконец, вы можете определить контрольное ограничение, сравнивающеезначения в более чем одном столбце. Это полезно для "проверки на реалистич-ность" тех значений, которые имеют некоторую логическую связь друг с дру-гом. Ни одна из созданных к этому моменту таблиц не содержит столбцов,подходящих для такой проверки. В приведенном ниже примере показано, каксоздать ограничение для фиктивной таблицы EMPLOYEE, гарантирующее,что текущая зарплата служащего не меньше его прошлогодней зарплаты, но ине превышает ее более чем на 25%:

ALTER TABLE employee ADD (CONSTRAINT realistic_current_salary CHECK (salary BETWEEN prior_j/ear_salary AND (prior_year_salary*l . 25) )

Разрешение и запрещениесуществующих ограничений

Бывают ситуации, когда полезно временно запретить ограничения, а впо-следствии снова их разрешить. Например, при загрузке данных из внешнегоисточника вы можете заранее знать, что некоторые из них не удовлетворяютопределенному ограничению, но предпочесть устранение ошибок средствамиOracle. В подобных случаях следует использовать команду ALTER TABLE, что-бы временно запретить ограничение, не удаляя его. Затем вы сможете загрузитьданные с нежелательными значениями, исправить их и снова разрешитьограничение.

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

ALTER TABLE имя^таблицы DISABLE CONSTRAINT имя_огрантения;

Команда разрешения ограничения имеет похожий синтаксис:

ALTER TABLE имя_таблицы ENABLE CONSTRAINT имя_ограничения;

Чтобы увидеть, к чему приводит запрещение и разрешение ограничений,введите показанные ниже команды. Первая команда пытается вставить запись,в которой значение LAST_STOCK_DATE не удовлетворяет ограничению наданный столбец. Следующая команда запрещает ограничение, и вторая попыт-ка вставить запись завершается успехом. Однако вновь разрешить ограничениене удается, поскольку в таблице есть запись со значением, не удовлетворяю-щим условию ограничения. После изменения значения ограничение успешновосстанавливается. По мере ввода команд сравнивайте результаты с показан-ными на рис. 6.11.

INSERT INTO plsq!101_product VALUES (

'Red Snaphoo1, 1.95, 10, '30-DEC-01')

ALTER TABLE plsql!01_product DISABLECONSTRAINT reasonable_stock_date;

INSERT INTO plsql!01_product VALUES ('Red Snaphoo', 1.95, 10, '30-DEC-01')

Индексы и ограничения 191

ALTER TABLE plsql!01_product ENABLE CONSTRAINTreasonable stock date;

UPDATE plsql!01_product

SET last_stock_date = '31-DEC-01'WHERE last_stock_date = '-30-DEC-01';

ALTER TABLE plsql!01_product ENABLE CONSTRAINT

reasonable stock date;

Ж Oiacle SQL-Plus HHBi

File Edit Search Qpfons HelpSQL> IHSERT INTO plsqll01 product UflLUES (2 'Red Snaphoo

1, T.95, 10, '30-DEC-01

1)

3 ;INSERT INTO plsqll01_product UflLUES (

К

ERROR at line 1:ORA-02290: check constraint (PLSQL1B1.REflSONfiBLE_STOCK_DflTE) violated

;; . • • • . - .

SQL>

SQL> flLTER TABLE plsqll01_product DISflBLE CONSTRAINT reasonable_stock_date;

Table altered.

SQL> INSERT INTO plsql101_product UflLUES (

2 'Red Snaphoo', 1.95, 10, '3B-DEC-011)

3

•1 row created.

SQL>SQL> ALTER TABLE plsqll01_product ENABLE CONSTRAINT reasonable_stock_date;ALTER TABLE plsqll01_product ENABLE CONSTRAINT reasonable_stock_date*

ERROR at line 1:

ORA-B2293: cannot enable (PLSQL101.REASONABLE_STOCK_DATE) - check constraint

violated

.SQL>

SQL> UPDATE plsqll B1_product

2 SET last stock_date = '31-DEC-01'3 WHERE lasOtock_date = 'Зв-DEC-ei';

1 row updated.

SQL>SQL> ALTER TABLE plsqll 01 product ENABLE CONSTRAINT reasonable_stock_date;

Table altered.

SQL>

Рис. 6.11. Последствия запрещения и разрешения ограничений .

192 Глава6

Изменение и удалениесуществующих ограничений

Жизнь непредсказуема, требования меняются, поэтому рано или поздновам придется модифицировать или удалять существующие ограничения. На-пример, очень часто требуется изменять ограничение NULL на NOT NULL инаоборот. Синтаксис команды, разрешающей столбцу принимать null-значе-ния, имеет следующий вид:

ALTER TABLE имя_таблицы MODIFY (имя_столбца NULL);

Если нужно, чтобы столбец перестал принимать null-значения, использует-ся следующий синтаксис:

ALTER TABLE имя_таблицы MODIFY (имя_столбца NOT NULL);

Чтобы увидеть этот подход в действии, введите следующие команды и срав-ните полученные результаты с теми, что показаны на рис. 6.12:

ALTER TABLE plsql!01_person MODIFY (first_name NULL);

ALTER TABLE plsql!01_person MODIFY (last_name NULL);

Я НЕЗ£ite Edit Search Options Help

SQL> ALTER TABLE plsql101_person MODIFV (firstjiane NULL);

Table altered.

SQL> ALTER TABLE plsqll01_person MODIFV (last_nane NULL);

Table altered.

SQL>

Рис. 6.12. Изменение статуса ограничения NULL

- : ' •••• • - ' • ' 'Если нужно совсем удалить ограничение, используется следующий

синтаксис:

ALTER TABLE имя_таблицы DROP CONSTRAINT имя_ограничения;

СоветИмейте в виду, что удаление ограничения — это необратимоедействие. Если есть вероятность, что ограничение сновапотребуется, его лучше запретить, а не удалять.

Эта техника иллюстрируется приведенной ниже серией команд. Введите ихи сравните результаты с показанными на рис. 6.13.

Индексы и ограничения _______ 193

Я 1-Е£ie:£(Jt Search Qptos: Help : '

SQL> INSERT INTOplsql101_product UALUES (2 'Blue Snaphoo'. 1.95, 10, •3D-DEC-011)3 ;

•Blue Snaphoo', 1.95, 18, •3B-DEC-B1')*

ERROR at line 2:ОНЙ-0229В: check constraint (PLSQL1B1.REASONABLE_STOCK_DATE) violated

SQL>SQL> ALTER TABLE plsq!1B1_product DROP CONSTRAINT reasonable_stock_date;

Table altered.

SQL>SQL> INSERT INTO plsql101_product UALUES (

2 'Blue Snaphoo1. 1.95, 1B. '3B-DEC-B1

1)

3 ;-,' . . • ' , ' ; , - ' • • • • " • ' j -

1 row created.

SQL> |

Рис. 6.13. Последствия удаления ограничения

INSERT INTO plsqll01_product VALUES (

'Blue Snaphoo1, 1.95, 10, '30-DEC-01')

ALTER TABLE plsql!01_product DROP CONSTRAINT reasonable_stock_date;

INSERT INTO plsql!01_product VALUES (

'Blue Snaphoo', 1.95, 10, '30-DEC-01')

Где следует определять ограничения —в базе данных или приложении?

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

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

194 Глава 6

медленно работающие сетевые соединения (например, Интернет-приложе-ния) или обслуживают много клиентов.

Но что произойдет, если кто-то соединится с базой данных не через утверж-денные формы ввода? Вы видели, насколько легко соединиться с базой данныхчерез SQL*Plus, и это почти так же легко сделать при использовании офисныхприложений типа Microsoft Access или Excel. Если ограничения базы данныхустанавливаются только в формах ввода данных, то пользователь, соединив-шийся с базой данных другим способом, может внести изменения, которые необязательно удовлетворяют ограничениям. В результате могут возникнуть бо-льшие проблемы — например, в заказах будут значиться товары, которых боль-ше нет, или появятся служащие, начало работы которых датируется серединой1800-х годов. Помните: если что-то может быть сделано неправильно, оно будетсделано неправильно. Включение ограничений в базу данных — это единствен-ный способ гарантировать их выполнение независимо от того, как устанавли-вается соединение с базой данных.

С моей точки зрения, вопрос не в том, где устанавливать ограничения — винтерфейсных компонентах или на сервере базы данных. Ограничения на сер-вере необходимы, и точка. Вопрос в том, нужно ли их устанавливать также и винтерфейсных компонентах. Если ваше приложение будет обслуживать мно-гих пользователей или работать через медленное сетевое соединение, и приэтом требовать крайне малого времени отклика, подумайте о встраиванииограничений в интерфейсные компоненты.

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

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

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

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

подразумеваемые связи друг с другом. Названия товаров из таблицыPLSQL101_PRODUCT используются в каждой записи таблицыPLSQL101_PURCHASE, а личные коды из таблицы PLSQL101_PERSON упо-минаются в столбце SALESPERSON таблицы PLSQL101_PURCHASE. Связиэтих типов составляют основу реляционных баз данных. Они позволяют вво-дить данные в одну таблицу, а затем ссылаться на них в других таблицах, тем са-мым исключая необходимость повторного ввода.

Индексы и ограничения 195

На рис. 6.14 показано логическое представление связей между таблицами.Линии, соединяющие одну таблицу с другой, обозначают связи, позволяющие,например, использовать название товара из записи о покупке для получениядополнительной информации об этом товаре, содержащейся в таблице това-ров. В примере, показанном на рис. 6.14, таблица PLSQL101_РИОВиСТявля-ется главной (parent), а таблица PLSQL101_PURCHASE — подчиненной (child).Связь "главный/подчиненный" имеют также таблицы PLSQL101_PERSON иPLSQL101_PURCHASE. При наличии такой связи на одну запись в главнойтаблице может ссылаться любое число записей подчиненной таблицы. В дан-ном примере продавец может осуществить сколько угодно продаж, поэтому наличный код из одной записи таблицы PLSQL101_PERSON может ссылатьсялюбое число записей таблицы PLSQL101_PURCHASE. С другой стороны, за-пись таблицы PLSQL101_PURCHASE может ссылаться только на одного про-давца, просто потому, что в ней не предусмотрено хранение более чем одногокода продавца. Это наиболее распространенный тип связи между таблицами,называемый связью "один ко многим" (one-to-many). В названии отражен тотфакт, что на одну запись главной таблицы могут ссылаться многие записи под-чиненной таблицы.

Как вы могли заметить, линии связей между таблицами оканчиваются с од-ной стороны сплошным кружком. Этот кружок обозначает "множественный"конец связи "один ко многим". Существует много стандартов для изображениясвязей такого рода. На рис. 6.14 использован стандарт IDEF1X, где "IDEF" рас-шифровывается как "Integration DEFinition for Information Modeling", a "IX" по-казывает, о каком стандарте идет речь (есть другие стандарты IDEF,описывающие иные типы диаграмм). Другим распространенным стандартом

PLSQL101 PRODUCT PLSQL101 PERSON

PRODUCT_NAME

PRODUCT PRICEQUANTITY ON HANDLAST STOCK DATE

PERSON_CODE

RRST NAMELAST NAMEHIRE DATE

PLSQL101 PURCHASE

PRODUCT NAMESALESPERSONPURCHASE DATEQUANTITY

Рис. 6.14. Связи таблиц PL/SQL 101 (стандарт IDEF1X)

196 Глава6

является IE, что расшифровывается как "Information Engineering". Этот стан-дарт, показанный на рис. 6.15, часто называют методологией "вороньей лапы"("Crow's Foot") из-за характерного значка, используемого для обозначения"множественного" конца связи "один ко многим".

Диаграммы, подобные тем, что показаны на рис. 6.14 и 6.15, известны какдиаграммы "сущность-связь" (Entity Relationship Diagrams, ERD). Их часто на-зывают моделями данных (data models). Они позволяют наиболее эффективноизображать структуру базы данных.

' . N .

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

Чтобы между двумя таблицами существовала связь, должны быть выполне-ны два условия:

• Главная таблица должна содержать столбец (или группу столбцов),уникально идентифицирующий каждую запись.

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

Например, в таблице PLSQL101_PRODUCT каждый товар имеет свое на-звание. Это значение (название товара) уникально идентифицирует каждую за-пись о товаре. Оно называется первичным ключом (primary key) таблицы.Первичный ключ является основным средством для ссылок на записи табли-цы. В таблице PLSQL101_PERSON первичным ключом служит личный код.

PLSQL101_PRODUCT PLSQL101 .PERSONPRODUCT NAMEPRODUCT PRICEQUANTITY ON HANDLAST STOCK DATE

PERSON CODEFIRST NAMELAST NAMEHIRE'DATE

IIA A

PLSQL101 PURCHASEPRODUCT NAMESALESPERSONPURCHASE DATEQUANTITY

Рис. 6.15. Связи таблиц PL/SQL 101 (стандарт IE)

Индексы и ограничения 197

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

Чтобы использовать первичный ключ главной таблицы для связывания таб-лиц, необходимо поместить ссылку на него в подчиненную таблицу. Это осу-ществляется путем включения в подчиненную таблицу столбца (или группыстолбцов), имеющего точно такой же тип данных, как и у первичного ключаглавной таблицы. Когда вам нужно создать в подчиненной таблице запись,ссылающуюся на запись главной таблицы, вы включаете в эту запись значе-ние^) первичного ключа. Например, чтобы определить, какой товар был куп-лен в результате транзакции, необходимо включить в запись о транзакцииназвание или номер товара, в зависимости от того, что используется в качествепервичного ключа главной таблицы.

Столбцы подчиненной таблицы, содержащие значения первичного ключа изглавной таблицы, называются внешними ключами (foreign keys). Внешний ключпозволяет подчиненной записи ссылаться на главную запись. Например, в таб-лице PLSQL101_PURCHASE столбец названий товаров является внешним клю-чом по отношению к таблице PLSQL101_PRODUCT. Подобно этому, столбецпродавцов в PLSQL101_PURCHASE является внешним ключом по отношениюк таблице PLSQL101_PURCHASE. На приведенной ниже иллюстрации показа-на модель данных тестовых таблиц, где внешние ключи обозначены как "(FK)".

PLSQL101 PRODUCT PLSQL101 PERSON

PRODUCT NAME

PRODUCT PRICEQUANTITY ON HANDLAST STOCK DATE

.

PERSON.CODE

FIRST NAMELAST NAMEHIRE DATE

*'•*.PLSQL101 PURCHASE

PRODUCT NAME (FK)SALESPERSON (FK)PURCHASE DATEQUANTITY

198 Глава 6

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

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

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

ALTER TABLE имя_таблщыADD PRIMARY KEY (имя_&иолбца_1, имя_столбца_2,...);

j|| Примечание> «*"•" Когда вы создаете первичный ключ, Oracle автоматически индексирует

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

Чтобы создать первичный ключ по столбцу PLSQL101_PRODUCT, введитеследующую команду и сравните результаты с показанными на рис. 6.16:

ALTER TABLE plsql!01_productADD PRIMARY KEY (product_name);

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

ALTER TABLE plsql!01_person

ADD PRIMARY KEY (person_code);

ALTER TABLE plsql!01_purchase

ADD PRIMARY KEY (product_name,salesperson,

purchase_date

f. Oracle SQL'Plus

File Edit Search Qptions Help

SQL> ALTER TABLE plsqll Byproduct2 ADD PRIMARV KEV (product_name);

Table altered.

SQL>

Рис. 6.16. Добавление первичного ключа к существующей таблице

Индексы и ограничения 199

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

CREATE TABLE plsql!01_product2 (

product_name VARCHAR2(25) PRIMARY KEY,

product_price NUMBER (4,2),

quantity_on_hand NUMBER (5, 0) ,

last stock date DATE

Создание ограничения внешнего ключаПервичные и внешние ключи — это физические компоненты, без которых

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

Фактически эта проблема уже присутствуете ваших тестовых таблицах. Вве-дите следующий код, сравните результаты с рис. 6, 1 7 и попробуйте определить,в какой подчиненной записи указан несуществующий товар:

SELECT product_name

FROM plsql!01_product

ORDER BY product_name;

SELECT DISTINCT product_name

FROM plsql!01_purchaseORDER BY product_name;

Каквидите, таблица РЬ80Ь101_РиК.СНА8Есодержитзаписьо транзакциидля "Round Snaphoo", хотя такого товара нет в таблице PLSQL101_PRODUCT.Прежде чем вы сможете создать ограничение внешнего ключа для таблицы по-купок, вам потребуется изменить название товара. Однако сначала попробуйтесоздать ограничение, ничего не меняя, чтобы увидеть реакцию Oracle. Командасоздания ограничения внешнего ключа имеет следующий синтаксис:

ALTER TABLE имя_подчиненной_таблицыADD CONSTRAINT имя_ограничения

FOREIGN KEY (имена_столбцов_подчиненной_таблицы)REFERENCES имя_главной_таблицы

200 Главаб

.* OiacleSUL'Plus НИ S3file £dil Search flptions Help

SQL> SELECT product_name2 FROM plsq!1B1_product3 ORDER BV product_name;

PRODUCT NftME

Anodized FraniFierBlue SnaphooChrome PhoobarExtra Huge Hega Phoobar +Mediun UodgetRed SnaphooRound Chrome SnaphooSmall WidgetSquare Zinculator

9 rows selected.

SQL>SQL> SELECT DISTINCT product name

2 FROM plsql101_purchase3 ORDER BV product_name;

PRODUCT_NAME

Chrome PhoobarMedium UodgetRound SnaphooSnail Widget

SQL>

Рис. 6.17. Ручной поиск подчиненной записи, которой не сопоставленаглавная запись

Применение этого синтаксиса к таблицам товаров и покупок даст нам сле-дующую команду:

ALTER TABLE plsql!01_purchaseADD CONSTRAINT plsql!01_purchase_fk_product

FOREIGN KEY (product_name)REFERENCES plsqll01_product

r

Введите ее, и вы увидите результаты, показанные на рис. 6.18. Фраза "parentkey not found" в сообщении об ошибке говорит о том, что подчиненная таблицасодержит одно или несколько названий товаров, отсутствующих в главной таб-лице. Прежде чем ограничение заработает, необходимо обновить некоррект-ное название товара в подчиненной таблице. Чтобы сделать это и повторитьсоздание ограничения, введите следующие команды:

Индексы и ограничения 201

* Oiacle SOL'Plus ЯНЕЗfile £dir S»«ch Option», HetfS«;,' ц

SQL> ALTER TABLE plsqll61 purchase -J2 ADD CONSTRAINT plsqlToi_purchase_fk_prodiict

:.

3 FOREIGN KEV (product папе)4 REFERENCES plsqll81 product;

ALTER TABLE plsqllB1_purchase*ERROR at line 1:ORA-02298: cannot enable (PLSQL181.PLSQL101_PURCHASE_FK_PRODUCT) - parent keynot found

SQL>

Рис. 6.18. Попытка создать ограничение внешнего ключапри несовпадении главных и подчиненных значений

UPDATE plsql!01_purchase

SET product_name = 'Round Chrome Snaphoo'

WHERE product_name - 'Round Snaphoo';

ALTER TABLE plsql!01_purchase

ADD CONSTRAINT plsql!01_purchase_fk_product

FOREIGN KEY. (product_name)

REFERENCES plsql!01_product;

Чтобы протестировать новое ограничение, попробуйте ввести следующуюзапись:

INSERT INTO plsql!01_purchase VALUES (

'Small Widgee', 'CA', '17-JUL-03', 1)

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

INSERT INTO plsqlldl_purchase VALUES (

'Small Widget', 'CA1, '17-JUL-03', 1)

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

202 Глава 6

* Oiacle SQL-Plus НбШFife Edk Search fiptions Help

SQL> INSERT INTO plsql1B1_purcliase UftLUES (2 'Snail Uidgee', 'Cfl1, •17-JUL-831. 1)

ERROR at line 3:ORfl-02291: integrity constraint <PLSqL101.PLSQLiei_PURCHflSE_FK_PRODUCT)violated - parent key not foundf У

• - /

SQL>

Рис. 6.19. Попытка вставить подчиненную запись при отсутствиисоответствующей главной записи

ПримечаниеКак правило, при создании ограничения внешнего ключа вамдостаточно указать в команде ALTER TABLE имя подчиненнойтаблицы, имена столбцов ее внешнего ключа и имя главнойтаблицы. Столбцы первичного ключа главной таблицы указывать ненужно, поскольку Oracle сможет определить их по имени таблицы.Но если столбцы расположены в разном порядке, их необходимоперечислить и для подчиненной, и для главной таблицы.

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

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

SELECT имя_таблицы_1.имя_столбца,имя_таблчцы_2. имя_столбца

FROM имя_таблицы_1,имя_таблицы_2

WHERE имя_главной_таблицы.первтный_ключ =имя_подчиненной_таблицы.внешний_ключ

5

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

Индексы и ограничения 203

.'приведенном примере синтаксиса фигурируют четыре обобщенных имени:ИМЯ_ТАБЛИЦЫ_1, ИМЯ_ТАБЛИЦЫ_2, ИМЯ_ГЛАВНОЙ_ТАБЛИЦЫ иИМЯ_ПОДЧИНЕННОЙ_ТАБЛИЦЫ. На самом деле эти четыре имени отно-сятся только к двум таблицам. Причина, по которой я использовал более одно-го имени на таблицу, состоит в следующем: перечислять выбираемые столбцыможно в любом порядке — сначала из главной таблицы, или сначала из подчи-ненной таблицы, или вообще вперемешку; это не играет роли, пока вы указы-ваете, откуда берется каждый столбец (предваряя имя столбца именем еготаблицы с точкой). Но когда вы пишете конструкцию WHERE, важно указать,какие столбцы являются первичным ключом главной таблицы, а какие — внеш-ним ключом подчиненной таблицы. Описывая в операторе SELECT связь "глав-ный/подчиненный", вы даете Oracle понять, как нужно связыватьинформацию из главной таблицы с информацией из подчиненной таблицы.

Давайте применим эту теорию на практике, отобразив список покупок сполными именами продавцов. Это можно сделать, использовав следующуюкоманду:

- у

SELECT plsql!01_purchase.product_name,

plsqll01_person.last_name,

plsql!01_person.first_name,

pisql!01_purchase.quantity

FROM plsql!01_purchase,

plsql!01_person

WHERE plsql!01_person.person_code = plsql!01_purchase.salesperson

, .Сравните свои результаты с показанными на рис. 6.20.Написание команды, которая комбинирует данные из более чем одной таб-

лицы в единый список, называется созданием соединения (join). Самое важное,что нужно помнить о соединении, заключается в следующем: вы должны вклю-чать в оператор SELECT конструкцию WHERE, описывающую, как связаны другс другом первичный ключ главной таблицы и внешний ключ подчиненной таблицы.Если этого не сделать, Oracle соединит каждую запись главной таблицы с каж-дой записью подчиненной таблицы, результатом чего может стать очень иочень длинный список. (Подобная ошибка может резко замедлить работу базыданных на время обработки неправильно написанного запроса, поэтому ее сле-дует всячески избегать.) Чтобы увидеть, к чему приводит создание соединениябез конструкции WHERE, введите следующий код и сравните свои результатыс показанными на рис. 6.21.

SELECT plsqll01_purchase.product_name,

plsqll01_person.last_name,

plsql!01_person.first_name,

plsql!01_purchase.quantity

FROM plsql!01_purchase,

plsql!01_person

i

Для пяти записей в таблице PLSQL101_PERSON и семи записей в таблицеPLSQL101_PURCHASE соединение даст 35 строк совершенно бесполезных

204 Глава 6

: - ' • ' I - -i : '

ЯК Ш * Oracle SQL'Plus низойти£Je £d* Search Qptions Help

SQL> SELECT plsqll U1_purchase . produc t_name , .d2 plsqll fl1_per son. las t_nane, — J3 plsql101_person.first_nane,4 plsql101 purchase. quantity5 FROM plsqll 01_purchase,6 plsqll 01_person7 WHERE plsqll B1_person.person_code - plsql101_purchase. salesperson8 ;

PRODUCT_HAME

Small WidgetRound Chrome SnaphooSmall WidgetChrome PhoobarSmall WidgetMedium WodgetMedium Wodget

7 rows selected.

SQL> |

у

LAST_NAME

AtlasAtlasAtlasAndersonAndersonBarkenhagenBaxter

•' •"•''

FIRST_NAME

CharleneCharleneCharleneGaryGaryBobbyLaren

• • • ;-' ' •'•

QUANTITY

151Zв

7520

•"•'' - '"'

'"•; >

•f'-

l

Рис. 6.20. Оператор SELECT, соединяющий записи из двух таблиц

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

В одном операторе SELECT можно соединять записи из многих разных таб-лиц. Для этого достаточно: (а) установить между таблицами логические связипервичного/внешнего ключа, и (Ь) определить каждую из этих связей в конст-рукции WHERE оператора SELECT.

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

SELECT plsq!101_purchase.product_name,

plsql!01_product.product_price,pisql!01_purchase.quantity,plsql!01_person.last_name

plsql!01_product,plsql!01_person,

plsqll01_purchase

plsqll01_product.product_name = plsql!01_purchase.product_nameand

plsql!01_person.person_code = plsql!01_purchase.salesperson

FROM

WHERE

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

Индексы и ограничения 205

J-* tlrocle SQL'Plus

£!e £dit Seech fipdoru Helpтшщ

SQL> SELECT plsql101_purchase.product_name, л.2 plsql101_person.last_nari)e. —3 plsql1B1_person.First_name,k plsql101~purchase. quantity5 FROM plsql101_purchase,6 plsq!101 person

i 7 ;

PRODUCT_NAME

Small WidgetMedium WodgetChrone PhoobarSmall WidgetHediuR WodgetRound Chrome SnaphooSmall WidgetSmall WidgetMedium WodgetChrone PhoobarSmall WidgetMedium WodgetRound Chrone SnaphooSmall WidgetSmall WidgetMedium WodgetChrone PhoobarSmall WidgetMedium WodgetRound Chrone SnaphooSmall WidgetSmall WidgetMedium WodgetChrome PhoobarSmall WidgetMedium WodgetRound Chrome SnaphooSmall WidgetSmall WidgetMedium WodgetChrome PhoobarSmall WidgetMedium WodgetRound Chrome SnaphooSnail Widget

35 rows selected.

SQL>

.iLJ

LflSTJMME

AtlasAtlasAtlasAtlasAtlasAtlasAtlasAndersonAndersonAndersonAndersonAndersonAndersonAndersonBarkenhagenBarkenhagenBarkenhagenBarkenhagenBarkenhagenBarkenhagenBarkenhagenBaxterBaxterBaxterBaxterBaxterBaxterBaxterMortonNortonNortonNortonMortonMortonNorton

FIRST_NflME

CharleneCharleneCharleneCharleneCharleneCharleneCharleneGaruGaruGaryGaryGaryGaryGaryBobbyBobbyBobbyBobbyBobbyBobbyBobbyLarenLarenLarenLarenLarenLarenLarenLindaLindaLindaLindaLindaLindaLinda

\

i•^

QUflNTITV

175

• , . 2; .;: • :

2051Л ,75282151 '1

75282051175282051 ., -175282051

, jf*

Рис. 6.21. Создание соединения без конструкции WHERE

206 Глава 6

л_во, дату последнего пополнения запасов купленного товара и фамилиюпродавца.

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

SELECT c.product_name,а.product_price,с.quantity,b.last_name

FROM plsql!01_product a,plsqll01_person b,plsql!01_purchase с

WHERE a.product_name = с.product_nameandb.person_code = с.salesperson

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

SELECT purc.product_name,

prod.product_price,

pure.quantity,

pers.last_name

FROM plsqll01_product prod,

plsql!01_person pers,

plsqll01_purchase pure

WHERE prod.product_name = purc.product_nameand

pers.person_code = pure.salesperson.i

Чтобы нажимать еще меньше клавиш, можно опустить имена таблиц для техстолбцов, имена которых уникальны среди таблиц, используемых для выбор-ки. Иными словами, если Oracle может посмотреть на имя столбца и опреде-лить, что оно существует только в одной из таблиц, вам не нужно указывать, вкакой таблице находится этот столбец. Однако я не рекомендую использоватьтакое упрощение в соединениях по двум причинам: во-первых, оно несколькозамедляет обработку (поскольку Oracle должен заглянуть во все таблицы, преж-

Индексы и ограничения 207

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

Внешние соединенияЧтобы понять, какую задачу решают соединения названного типа, введите

следующий код:

SELECT product_name FROM plsql!01_product ORDER BY product_name;

SELECT prod.product_name,

prod.product_price,

pure.purchase_date,

pure.quantity

FROM plsql!01_product prod,

plsql!01_purchase pureWHERE prod.product_name = purc.product_name

ORDER BY prod.product_name;

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

Иногда это желательно — например, когда вам нужно знать, что было прода-но. Однако во многих ситуациях вам потребуется (например) выводить полныйсписок товаров вместе с информацией о транзакциях, даже если для некоторыхтоваров транзакции не выполнялись. Чтобы получить такой результат, вы дол-жны сообщить Oracle, что не каждой записи главной таблицы может соответст-вовать запись в подчиненной таблице. Это делается путем помещения символа(+) после имени подчиненной таблицы в конструкции WHERE. В итоге коман-да принимает следующий вид:

SELECT product_name FROM plsql!01_product ORDER BY product_name;

SELECT prod.product_name,prod.product_price,purc.purchasejdate,pure.quantity

FROM plsql!01_product prod,plsq!101_purchase pure

WHERE prod.product_name = purc.product_name (+.)ORDER BY prod.product_name;

Введите этот код и убедитесь, что выходной листинг содержитназвания всехтоваров — даже тех, которые не продавались. Это называется созданием внеш-него соединения (outer join). Символы (+), помещаемые после имени одной изтаблиц в каждом из операторов имя_главной_таблицы.первичный_ключ =имя_подчиненной_таблицы.внешний_ключ конструкции WHERE, показывают,что таблица может не иметь записей, соответствующих каждой записи другойтаблицы. Иными словами, символы (+) ставятся после таблицы, для которойвместо совпадающего столбца могут быть выведены пробелы. Как правило, (+,).

8 Зак. 725

208 Глава 6

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

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

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

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

Таблица 1

Таблица 2

UNION

UNION AT All

Таблица 6.3. Операторы соединения.

Оператор соединения

UNION

UNION ALL

INTERSECT

MINUS

Получаемые результаты .

Все строки из обоих операторов SELECT; повторяющи-еся значения удаляются

Все строки из обоих операторов SELECT; повторяющи-еся значения показываются

Строки, которые возвращены и первым, и вторым опе-ратором SELECT

Строки, которые возвращены первым операторомSELECT, исключая те, которые возвращены вторымоператором

Индексы и ограничения 209

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

CREATE TABLE plsqll01_purchase_'archive (product_name VARCHAR2 (25) ,

salesperson VARCHAR2(3),

purchase_date DATE,

quantity NUMBER (4, 2)

INSERT INTO plsql!01_purchase_archive VALUES

('Round Snaphoo', 'BB', ' 21-JUN-01 ' , 10);

INSERT INTO plsqll01_purchase_archive VALUES

('Large Harf linger ', 'GA', '22-JUN-01', 50);

INSERT INTO plsql!01_purchase_archive VALUES

( 'Medium Wodget', 'LB', '23-JUN-01', 20);

INSERT INTO plsqll01_purchase_archive VALUES

('Small Widget' , 'ZZ', '24-JUN-02 '-, 80);

INSERT INTO plsql!01_purchase_archive VALUES

. ('Chrome Phoobar', 'CA', '25-JUN-02', 2) ;

INSERT INTO plsql!01_purchase_archive VALUES

('Small Widget', 'JT', '26-JUN-02', 50);

UNIONОператор соединения UNION предназначен для объединения данных из

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

Чтобы увидеть, как это делается, введите следующую команду и сравните еерезультаты с показанными на рис. 6.22:

SELECT product_name FROM plsql!01_purchase

ORDER BY product_name;

SELECT product_name FROM plsq!101_purchase_archive

ORDER BY product_name;

SELECT product_name FROM plsql!01_purchase

UNIONSELECT product__name FROM plsql!01_purchase_archive

ORDER BY product_name;

Обратите внимание, что каждая из таблиц содержит некоторые товары, от-сутствующие в другой таблице. Так, таблицаРЬ8рЫ01_РиК.СНА8Есодержитзапись о "Round Chrome Snaphoo", которого нет в таблице PLSQL101_PURCHASE_ARCHIVE, a "Large Harflinger" упомянут во второй таблице, ноотсутствует в первой. Список, выведенный оператором UNION, содержит обатовара, равно как и все остальные товары, присутствующие одновременно вдвух таблицах.

8*

210 Глава6

* Otacle SQL-Plus

£te £dit Search flptions H<*SQL> SELECT product_name FROM plsqll repurchase

2 ORDER BV product_nane;

PRODUCT НИНЕ

Chrome Phoobar

Medium Uodget

Medium Uodget

Round Chrome Snaphoo

Small Midget

Snail Widget

Small Widget

7 rows selected.

SQL> SELECT product_name FROM plsqliai_purchase_archiue

2 ORDER BV product_name;

PRODUCT НЙМЕ

Chrome Phoobar

Large HarFlinger

Medium Uodget

Round Snaphoo

Small Midget

Snail Midget

6 rows selected.

SQL> »

SQL> SELECT product_nane FROM plsq!101_purchase

2 UNION

3 SELECT product_nane FROM plsql181_purchase_archiue

4 ORDER BV product_nane;

PRODUCT NOME

Chrome Phoobar

Large HarFlinger

Medium Uodget

Round Chrome Snaphoo

Round Snaphoo

Small Widget

б rows selected.

SQL>

-iLJ

Рис. 6.22. Оператор соединения UNION

UNION ALLОператор соединения UNION ALL функционально похож на оператор

UNION, но он возвращает все возможные строки, а не только по одной строкедля каждого уникального значения. Чтобы увидеть, в чем состоит отличие, вве-дите следующую команду:

SELECT product_name FROM plsql!01_purchaseUNION ALL

SELECT product_name FROM plsql!01_purchase_archiveORDER BY product_name;

Индексы и ограничения _____ 211

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

INTERSECTОператор соединения INTERSECT возвращает только те значения, которые

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

SELECT product_name FROM plsq!101_purchase

INTERSECT

SELECT product_name FROM plsql!01_purchase_archive

ORDER BY product name;' ' ' • ' - • ' ' : ' •

MINUSОператор соединения MINUS противоположен оператору INTERSECT. Он

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

SELECT product_name FROM plsql!01_purchase

MINUS

SELECT product_name FROM plsql!01_purchase_archive

ORDER BY product_name;

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

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

Что такое подзапрос?Подзапрос — это обычный запрос SELECT, вложенный в оператор

SELECT, UPDATE или DELETE. Он используется в качестве источника дан-ных для раздела FROM или WHERE родительского оператора.

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

Типичные проблемы,решаемые с помощью подзапросов

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

212 Глава6

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

Однострочные подзапросыРассмотрим пример. Вы обнаружили, что самая последняя партия товара

Small Widget была перехвачена где-то по дороге, а вместо нее пришли дешевыеподделки. Вы хотите узнать, что еще должно было прийти в тот день, чтобыпроверить и эти товары. Вы не знаете, когда планировалось поступление SmallWidget, и поэтому не можете явно указать эту дату в конструкции WHERE. Од-нако можно запросить дату у Oracle и использовать ее так, как если бы она былавведена вручную. Для этого вы должны указать в конструкции WHERE опера- .тора SELECT, что необходимо получить дату последнего пополнения запасовSmall Widget, а затем использовать ее для фильтрации записей о других товарах.Введите следующую команду и сравните результаты с теми, что показаны нарис. 6.23:

SELECT *

FROM plsqllOl_J>roduct

WHERE last_stock_date = (SELECT last_stock_dateFROM plsg!101_product

WHERE product_name = 'Small Widget'

Чтобы лучше освоить эту технику, создайте новый оператор SELECT,возвращающий все записи о товарах с ценой, равной цене Red Snaphoo.

£ite £d» Search flptiohs H*SQL> SELECT "•'

2 FROM plsql1B1 product3 WHERE last_stock date - (4 SELECT last stock date5 FROM plsqltBI product6 WHERE product name - 'Snail Widget•7 )e ;

PRODUCT_NAHE PRODUCT_PR1CE QUflNTITV_ON_HflND LfiST_STOC

Snail Widget 99 1 15-JAH-03Chrone Phoobar SB 100 15-JftN-B3

SQL>

Рис. 6.23. Подзапрос, основанный на дате последнего пополнениязапасов

Индексы и ограничения 213

Ваши результаты должны совпадать с показанными на рис. 6.24. Вы должны непросто подставить значение 1.95 в конструкцию WHERE; пусть оператор самопределит, какова цена Red Snaphoo.

Ключевой характеристикой только что написанных подзапросов являетсято, что они возвращают единственное значение. Например, когда вы пишетеподзапрос для определения даты последнего пополнения запасов Small Widget,может быть возвращено только одно значение, поскольку для хранения этойдаты в таблице товаров отведен только один столбец. В этом случае в конструк-ции WHERE родительского оператора можно уверенно ставить знак равенства.(Если бы подзапрос потенциально мог возвращать несколько значений, знакравенства нельзя было бы использовать, поскольку дата последнего пополне-ния запасов в родительском операторе всегда имеет только одно значение.)Подзапрос такого типа называется однострочным подзапросом (single-row sub-query), поскольку он может возвращать только одну строку результатов. (Разу-меется , родительский оператор может возвращать любое число строк на основеодного ответа подзапроса.)

Подзапрос и родительский оператор могут ссылаться на совершенно разныетаблицы. Допустим, вам нужно узнать обо всех продажах, которые выполнилГари Андерсон. Вы знаете только имя продавца, но не код, под которым он зна-чится в таблице покупок. (Для наших тестовых таблиц, где личный код — этопросто инициалы, такая постановка задачи не имеет особого смысла, но в реа-льной базе данных наверняка использовался бы номер, не имеющий очевид-ной связи с именем, и догадаться, кого он представляет, было бы непросто.)Следовательно, нужно сделать так, чтобы оператор SELECT сам нашел кодГариДндерсона, а потом показал записи о его продажах. Вот соответствующаякоманда:

SELECT * FROM plsql!01_purchase

WHERE salesperson = (

SELECT person_code

FROM plsq!101_person

WHERE first_name = 'Gary' AND last_name = 'Anderson'

* Oiacle SQL-Plus НИИ

£!e fid» £e«qh Options H*'':•' : ; ; :

dPRODUCT_HflME PRODUCT_PRICE QUflHTITV_ON_HflHO LftST_STOC

Red Snaphoo 1.95 10 31-DEC-01Blue Snaphoo 1.95 10 30-DEC-Q1

SQL>SQL>SQL>

Рис. 6.24. Подзапрос, основанный на цене товара

214 Глава 6

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

SELECT *FROM plsqll01_product

WHERE product_price > (SELECT AVG(product_price)

FROM plsql!01_product

Многострочные подзапросыКак вы могли догадаться, многострочный подзапрос (multirow subquery) — это

подзапрос, который может возвращать более одной строки результатов. Для та-ких подзапросов нельзя выполнять сравнение с помощью знака равенства; не-обходимо использовать функцию IN.

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

SELECT *

FROM plsql!01_purchase

ORDER BY product_narae;

SELECT *

FROM plsql!01_productWHERE product_name NOT IN (

SELECT DISTINCT product_name »

FROM plsql!01_purchase

ORDER BY product_name

Как упоминалось выше, подзапросы можно использовать также в операто-рах UPDATE и DELETE. Предположим, что вам дано указание снизить на 10%цены всех товаров, не пользующихся спросом. Это можно сделать единствен-ной командой UPDATE, поместив в ее конструкцию WHERE подзапрос, опре-деляющий, какие товары не продавались:

SELECT * FROM plsql!01_product;

UPDATE plsqllOl_product

SET product_price = product_price * .;9

WHERE product_name NOT IN (

SELECT DISTINCT product_name

FROM plsql:101_purchase

SELECT * FROM plsql!01_product;

Индексы и ограничения 215

* Oiacle SQf Plus

File Edit Jearch Options HelpSQL> SELECT *

2 FROM plsql1B1_purchase3 ORDER BV pi-oduct_name;

ига is

PRODUCT_NflHE SflL PURCHflSE_ QUflNTITV

Chrome Phoobar Си 14-JUL-83

Medium Uodget BB 14-JUL-B3

Mediun Uodget LB 15-JUL-B3

Round Chrome Snaphoo СЙ 16-JUL-B3

Small Widget СЙ 14-JUL-83

Small Widget Си 15-JUL-03

Snail Widget Си 17-JUL-03

7 rows selected.

SQL>SQL> SELECT *

2 FROM plsquei product

3 WHERE product name HOT IN (

4 SELECT DISTINCT product name

5 FROM plsq!1B1 purchase

4 )7 ORDER BV product папе8 '• :,. . '•

27520

S181

PRODUCT_NfiM£ PRODUCT_PRICE qUflNTITV_DH_HflNO LflST_STOC

flnodized FramiFierBlue SnaphooExtra Huge Mega Phoobar *Red SnaphooSquare Zinculator

SQL>

491.959.951.9545

10 3B-DEC-011234 l5-JflN-0»i10 31-OEC-011 31-OEC-82

Рис. 6.25. Использование многострочного подзапроса для поисканесовпадающих записей

Как видите, в результате выполнения этой команды были изменены ценытолько тех товаров, которые ни разу не были проданы. Удобно, не правда ли?

Подзапросы, возвращающие болееодного столбца

Все подзапросы, которые вы видели до сих пор, извлекали только один стол-бец данных. Однако они могут возвращать и несколько столбцов. Эту возмож-ность демонстрирует приведенный ниже код, который анализирует названиятоваров и даты покупок из таблицы PLSQL10 1_PURCHASE, чтобы вернуть за-писи только о самых последних покупках каждого товара. (Проводя собеседо-вание с людьми, работа которых будет связана с PL/SQL, я задаю ряд тестовыхвопросов. Один из них требует знания описанной техники; очень немногие от-вечают правильно.)

216 Глава 6

SELECT *FROM plsql!01_purchase

ORDER BY product_name, purchase_date;

SELECT *

FROM plsqllOi_purchaseWHERE (pro'duct_name, purchase_date)IN (SELECT product_name, MAX(purchase_date)

FROM plsql!01_purchase

GROUP BY product_name

ИтогиЭта глава основательно подготовила вас к работе с индексами и ограничени-

ями в Oracle. Индексы баз данных похожи на книжные индексы: они содержатключевую информацию из всех строк таблицы вместе с указателями на этистроки, что позволяет намного быстрее находить строку с заданным значени-ем — если, конечно, оно присутствует в индексе. После того как вы создали ин-декс, Oracle автоматически поддерживает его синхронизацию с таблицей.Любые операции INSERT, UPDATE или DELETE над этой таблицей будут ав-томатически изменять ее индекс, и любая команда SELECT будет выполнятьсяс привлечением индекса, если он содержит требуемые столбцы. Добавлениеили удаление индексов не влияет на операции с таблицей — любая использо-вавшая ее программа по-прежнему будет работать, хотя и более медленно. Еслиудалить таблицу, все ассоциированные с ней индексы также будут удалены, по-скольку в отсутствие таблицы индекс бесполезен.

Индексы уменьшают время отклика команд, выполнение которых требуетсчитывания содержимого таблицы. Добавление индексов к таблице не ускоря-ет выполнение команд INSERT; реально ввод данных в индексированные таб-лицы замедляется, поскольку данные необходимо вставлять также и виндекс(ы). Таким образом, использование индексов — это компромисс. Онизамедляют ввод данных, но ускоряют их считывание.

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

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

Индексы и ограничения 217

INSERT или UPDATE нарушает ограничение, Oracle прерывает ее выполне-ние, производит откат и выдает сообщение об ошибке.

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

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

Вопросы1. Что из перечисленного не относится к достоинствам индексов?

A. Ускоренное выполнение команд INSERT

B. Ускоренное выполнение команд UPDATE

C. Ускоренное выполнение команд SELECT

D. Ускоренное выполнение команд DELETE

2. На какой строке будет выдано сообщение об ошибке при выполненииэтой команды?

CREATE INDEX plsqll01_purchase_pk ON plsqll01_purchase (product_name,salesperson,purchase_date);

A. 1

B-2 ,

с. зD.4

E. Команда будет выполнена успешно

3. Какой из перечисленных индексов лучше всего подходит для столбца свысокой кардинальностью?

А. Составной

218 Глава 6

/

B. В*-дерево

C. Битовый

D. Другой

4. Какая из перечисленных команд позволила бы гарантировать, что то-вар, название которого вводится в запись о покупке, существует в таб-лице товаров?

A. CREATE INDEX имя_индекса ON имя_таблицы(имя_столбца)\

B. ALTER TABLE имя_таблицы MODIFY (имя_столбца NOT NULL);

C. ALTER TABLE имя_таблицы ADD CONSTRAINT имя_огрантенияUNIQUE (имя_столбца);

D. ALTER TABLE имя_таблицы ADD CONSTRAINT имя_ограниченияCHECK/имя_столбца условие,);

E. ALTER TABLE имя_таблицы ADD CONSTRAINT имя ограниченияFOREIGN KEY (имя_столбца) REFERENCES имя__главной таблицы;

5. Что будет выведено в результате выполнения приведенной ниже коман-ды, если таблица 1 содержит пять записей, а таблица 2 — десять записей?

SELECT иия_ таблицы_ 1.имя_ столбца_1,имя таблицы_2.имя_столбца_2

FROM имя_ таблицы_1,имя таблицы_2

A. Пять записей из таблицы 1.

B. Десять записей из таблицы 2.

C. Пятнадцать записей с данными из обеих таблиц.

D. Пятьдесят записей с данными из обеих таблиц.

E. Количество выведенных записей будет зависеть от того, сколько за-писей таблицы 1 имеют те же значения, что и записи таблицы 2.

Ответы на вопросы1. А. Ускоренное выполнение команд INSERT

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

2. Е. Команда будет выполнена успешно.

Объяснение Обратитесь к разделу "Как создавать индексы", чтобыосвежить в памяти синтаксис команды CREATE INDEX.

Индексы и ограничения 219

3.В. В*-дерево

Объяснение Составной индекс — это просто индекс по многимстолбцам; кардинальность столбцов тут ни при чем. Битовые индексыпредназначены для столбцов с низкой кардинальностью, т.е. с малымколичеством различных значений. Вариант D ("другой") бессодержате-лен, поэтому остается только индекс В*-дерева, предназначенный длястолбцов с большим количеством различных значений.

4. Е.ALTER TABLE имя_таблицыADD CONSTRAINT имя_огрантенияFOREIGN KEY (имя_столбца) REFERENCES имя_главной таблицы;

Объяснение Подсказкой служит слово "REFERENCES" в команде.Его наличие существенно при создании механизма поддержания ссы-лочной целостности между двумя таблицами.

5. D. Пятьдесят записей с данными из обеих таблиц.

Объяснение Поскольку оператор SELECT производит выборку издвух таблиц, но не содержит конструкции WHERE, поясняющей, каких следует соединять, результатом будет декартово произведение. Каж-дое значение столбца 1 таблицы 1 будет соединено с каждым значениемстолбца 2 таблицы 2, что даст 5*10, т.е. 50 записей.

'

4

Глава

Другие полезныесредства Oracle

222 Глава?

В этой главе собраны описания различных полезных средств, знакомство скоторыми дополнит ваше знание Oracle в части SQL. Вы узнаете, как перено-сить данные между таблицами; переименовывать таблицы и изменять их струк-туру; использовать словарь данных Oracle; создавать и использоватьпредставления, последовательности и синонимы. Освоив материал этой главы,вы хорошо подготовитесь к изучению последующих глав, посвященных про-граммированию на PL/SQL.

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

DROP TABLE plsql!01_purchase;

DROP TABLE plsql!01_product;

DROP TABLE plsql!01_person;

DROP TABLE plsqll01_old_item;

DROP TABLE plsqll01_purchase_archive; '

CREATE TABLE plsql!01_persbn (

person_,code VARCHAR2O) PRIMARY' KEY,

first_name VARCHAR2(15),last_name VARCHAR2(20),

hire_date DATE

);

• • . •• : • -CREATE INDEX plsql!01_person_name_index

ON plsql!01_person(last_name, first_name);

ALTER TABLE plsql!01_person

ADD CONSTRAINT plsql!01_person_unique UNIQUE (

first_name,

last name,

hire_date

»r

INSERT INTO plsql!01_person VALUES

CCA', 'Charlene', 'Atlas', 'Ol-FEB-02') ;INSERT INTO plsql!01_person VALUES

('GA', 'Gary', 'Anderson', '15-FEB-02') ;INSERT INTO plsql!01_person VALUES

('BB', 'Bobby', 'Barkenhagen', '28-FEB-02');INSERT INTO plsql!01_person VALUES

('LB', 'Laren', 'Baxter', 'Ol-MAR-02');INSERT INTO plsql!01_person VALUES (

'LN', 'Linda1, 'Norton', 'Ol-JUN-03');

CREATE TABLE plsqll01_product (

product_name VARCHAR2(25) PRIMARY KEY,product_price NUMBER(4,2),

quantity_on_hand NUMBER(5,0),

Другие полезные средства Oracle 223

laststock_date DATE

ALTER TABLE plsql!01_product ADD CONSTRAINT positive_quantity CHECK(

quantity_on_hand IS NOT NULL

AND

quantity_on_hand >= 0

INSERT INTO plsql!01_product VALUES('Small Widget

1, 99, 1, 45-JAN-03 ' ) ;

INSERT INTO plsql!01_product VALUES('Medium Wodget', 75, 1000, ' 15-JAN-02 ' ) ;

INSERT INTO plsql!01_product VALUES

('Chrome Phoobar1, 50, 100, ' 15-JAN-03 ' ) ;

INSERT INTO plsql!01_product VALUES('Round Chrome Snaphoo

1, 25, 10000, null);

INSERT INTO plsql!01_product VALUES

('Extra Huge Mega Phoobar +', 9.95, 1234, ' 15-JAN-04 ' ) ;

INSERT INTO plsql!01_product VALUES ('Square Zinculator',45, 1, TO_DATE (' December 31, 2002, 11:30 P.M.',

'Month dd, YYYY, HH-.MI P.M.')

INSERT INTO plsql!01_product VALUES (

'Anodized Framifier', 49, 5, NULL);

INSERT INTO plsql!01_product VALUES (1 Red Snaphoo ', 1.95, 10, ' 31-DEC-01 ' ) ;

INSERT INTO plsql!01_product VALUES (

'Blue Snaphoo', 1.95, 10, '30-DEC-01')

CREATE TABLE pisql!01_purchase (

product_name VARCHAR2(25),salesperson VARCHAR2(3),

purchase_date DATE,quantity NUMBER(4,2)

ALTER TABLE plsql!01_purchase

ADD PRIMARY KEY (product_name,

salesperson,

purchase_date

ALTER TABLE plsql!01_purchase ADD CONSTRAINT- reasonable_date CHECK(

purchase_date IS NOT NULL

ANDTO_CHAR(purchase_date, 'YYYY-MM-DD

1) >= '2000-06-30'

224 Глава?

ALTER TABLE plsql!01_purchaseADD CONSTRAINT plsql!01_purchase_fk_product FOREIGN KEY

(product_name) REFERENCES plsql!01_product;

ALTER TABLE plsqll01_purchaseADD CONSTRAINT plsql!01_purchase_fk_person FOREIGN KEY(salesperson) REFERENCES plsql!01_person;

CREATE INDEX plsql!01_purchase_productON plsql!01_purchase(product_name);

CREATE INDEX plsql!01_purchase_salespersonON plsql!01_purchase(salesperson);

INSERT INTO plsql!01_purchase VALUES('Small Widget', 'CA', '14-JUL-03

1, 1) ;

INSERT INTO plsql!01_purchase VALUES('Medium Wodget', 'BB

1, '14-JUL-03

1, 75);

INSERT INTO plsql!01_purchase VALUES('Chrome Phoobar', 'GA', 44-JUL-03', 2) ;

INSERT INTO plsql!01_purchase VALUES('Small Widget

1, 'GA', 45-JUL-03', 8);

INSERT INTO plsql!01_purchase VALUES('Medium Wodget', 'LB', 45-JUL-03', 20);

INSERT INTO plsql!01_purchase VALUES('Round Chrome Snaphoo', 'CA', 46-JUL-03', 5);

INSERT INTO plsql!01_purchase VALUES ('Small Widget', 'CA', 47-JUL-03', 1)

UPDATE plsql!01_productSET product_price = product_price * .9WHERE product_name NOT IN (

SELECT DISTINCT product_nameFROM plsqllOl purchase

CREATE TABLE plsq!101_old_item (item_id CHAR (20),item_desc CHAR (25)

INSERT INTO plsq!101_old_item VALUES('LA-101

1, 'Can, Small');

INSERT INTO plsql!01_old_item VALUES('LA-102', 'Can, Large');

INSERT INTO plsql!01_old_item VALUES('LA-ЮЗ

1, 'Bottle, Small');

Другие полезные средства Oracle 225

INSERT INTO plsq!101_old_item VALUES

CLA-104', 'Bottle, Large');

INSERT INTO plsq!101_old_item VALUES

CNY-101', 'Box, Small1);

INSERT INTO plsq!101_old_item VALUES

('NY-102', 'Box, Large');

INSERT INTO plsq!101_old_item VALUES

('NY-ЮЗ', 'Shipping Carton, Small')

INSERT INTO plsq!101_old_item VALUES

CNY-104', 'Shipping Carton, Large')

CREATE TABLE plsqll01_purchase_archive (

product_name VARCHAR2(25),

salesperson VARCHAR2(3),

purchase_date DATE,

quantity NUMBER(4,2)

INSERT INTO plsq!101_purchase_archive VALUES('Round Snaphoo', 'BB', '21-JUN-01', 10);

INSERT INTO plsql!01_purchase_archive VALUES

('Large Harflinger', 'GA', '22-JUN-011, 50);

INSERT INTO plsql!01_purchase_archive VALUES

('Medium Wodget', 'LB', '23-JUN-01', 20);

INSERT INTO plsql!01_purchase_archive VALUES

('Small Widget', 'ZZ1, '24-JUN-02

1, 80);

INSERT INTO plsq!101_purchase_archive VALUES('Chrome Phoobar

1, 'CA', '25-JUN-02', 2);

INSERT-INTO plsql!01_purchase_archive VALUES

('Small Widget1, 'JT', '26-JUN-02', 50);

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

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

• Импорт данных из унаследованной системы Типичной задачей,возникающей при работе с SQL, является перенос данных изсуществующей системы в новую. Иногда существующая системапросто заменяется новой. В других случаях вам нужно отображать ипереносить в свою систему данные, полученные из внешнегоисточника. Зачастую исходные данные должны модифицироваться дозанесения в новые таблицы, что может потребовать использованиятаких функций, как UPPER, LOWER, LTRIM, RTRIM, SUBSTR,INSTR, TO_CHAR и DECODE.

• Загрузка итоговых значений в хранилище данных Основной функциейхранилища данных (data warehouse) является хранение предварительнополученных ответов на часто задаваемые вопросы — имеются в виду те

226 Глава?

вопросы, на которые можно ответить с помощью функцийЗиМ,COUNT, AVG, MIN и МАХ в сочетании с конструкциями GROUP BY.Ответы обычно хранятся в отдельном наборе таблиц, которыезаполняются путем выполнения SQL-запросов.

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

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

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

Л;•< -_ CREATE TABLE plsqllQl_purchase_log ( •

purchase_date DATE,

product_name VARCHAR2(25),

product_price NUMBER(4,2),

quantity NUMBER(4,2),

sales_first_name VARCHAR2(15),

sales_last_name VARCHAR2(2D)

)

;

Таблица PLSQL101_PURCHASE_LOG — это "плоское" представление наи-более важной информации о каждой покупке: даты продажи, названия, цены иколичества купленного товара, а также полного имени продавца. Подобнаятаблица хорошо подходит для получения ответов на вопросы типа: "Кто продалнаибольшее и наименьшее количество Red Snaphoo?" Помещение в одну таб-лицу всей информации, необходимой для ответа на такие вопросы, позволяетполучать ответы быстрее и гарантирует, что обращение к таблицам транзакцийне будет замедляться запросами от людей, которым нужно анализировать этитранзакции как группу.

Теперь, когдау вас есть таблица плоского файла для хранения записей, под-лежащих анализу, пора заполнить ее данными. Это будет сделано при помощикоманды INSERT, соединяющей записи из таблиц PERSON, PRODUCT иPURCHASE. Ее синтаксис выглядит следующим образом:

INSERT №ТОимя_таблицы (оператор SELECT

Другие полезные средства Oracle 227

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

INSERT INTO plsqll01_purchase_log (SELECT purc.purchase_date,

prod.product_name,prod.product_price,

pure.quantity,pers.first_name,pers.last_name

FROM plsql!01_product prod,plsql!01_person pers,

plsql!01_purchase pureWHERE prod.product_name = purc.product_name

AND

pers.person_code = pure.salesperson

SELECT * FROM plsqll01_purchase_log;

Fie £dit Search Options Help

2 SELECT pure. pur chase_date.3 prod.product_nane.ft prod.product_price,5 pure. quantity ,6 pers.First_name,7 pers. las t_nane8 FROM plsqllfll .product prod.9 plsq!101_person pers.

10 plsql101_purchase pure11 WHERE prod. product name * pure12 BHD13 pers. person code - pure.1» )15 ;

7 rows created.

SQL>SQL> SELECT » FROM plsq!101_purchase_log;

l

.product_nane

salesperson•

PURCHASED PROOUCT_MAME PROOUCT_PRICE quflHTITY S*LES_FIRST_NftM S

1it-JUL-B3 Small Uidget1H-JUL-03 Medium Wodget14-JUL-83 Chrome Phoobar15-JUL-83 Snail Widget1S-JUL-03 Medium Wodget16-JUL-03 Round Chrone Snaphoo17-JUL-G3 Snail Widget

99 1 Charlene Й75 75 Bobby В50 2 Gary и99 8 Gary Й75 28 Laren В25 5 Charlene Й99 1 Charlene и

7 rows selected.

SQL>

Рис. 7.1. Копирование записей из одной таблицы в другую

228 Глава?

Как видите, таблица PLSQL101_PURCHASE_LOG содержит удобные дляанализа наборы данных о каждой транзакции из таблицыPLSQL101_PURCHASE.

Создание новой таблицы на основеуже существующей

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

CREATE TABLE имя_новой_таблицы ASоператор SELECT

i

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

I *. Omcle SOL'Plus ИИ El

file £dil Se«ch Uptons НФ :';'•.•,SQL> CREATE TABLE plsqll 01_purchase_log2 AS .12 SELECT purc.purcbase_date, —3 prod.product_nane,U prod.product_price,5 pure. quantity,6 pers.first_nane,7 pers.last_nane8 FROM plsqll 01_product prod,9 plsqll 81 person pers,11 plsq!101_purchase pure11 WHERE prod. product name - pure. product nane12 AND13 pers.person_code - pure. salesperson

Table created.

SQL>SQL> SELECT « FROM plsqll 01_purchase_log2;

PURCHASE. PRODUCT_NAME

14-JUL-B3 Snail Widget14-JUL-D3 Medium Uodget14-JUL-03 Chrome Phoobar15-JUL-83 Snail Widget15-JUL-83 Medium Wodget16-JUL-B3 Round Chrome Snaphoo17-JUL-03 Snail Widget

7 rows selected.

SQL>

JJ

PRODUCT_PRICE

99755199752599

'

QUANTITY FIRST_NAME

1 charlene75 Bobby2 Gary8 Gary20 LarenS Gharlene1 Charlene

L

AВAAВAA

MРис. 7.2. Создание новой таблицы на основе одной или нескольких

существующих таблиц

Другие полезные средства Oracle 229

CREATE TABLE plsql!01_purchase_log2 AS

SELECT pure.purchase_date,prod.product_name,prod.product_price,pure.quantity,

pers.first_name,pers.last_name

FROM plsql!01_product prod,plsqll01_person pers,plsql!01_purchase pure

WHERE prod.product_name = purc.product_nameAND

pers.person_code = pure.salesperson

SELECT * FROM plsqll01_purchase_log2;

Переименование таблицВремя от времени приходится менять имена существующих таблиц. Это де-

лается очень легко. Вот соответствующий синтаксис:

RENAME старое_имя_таблицы ТО новое_имя_таблицы; ,

Примените его к одной из своих таблиц, введя следующую команду:RENAME plsql!01_purchase_log2 TO plsql!01_log;

Изменение структуры таблицыЗа время существования базы данных могут измениться бизнес-требования,

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

Добавление столбцовДобавлять к таблице столбцы можно в любой момент. Новые столбцы раз-

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

ALTER TABLE имя_таблицыADD имя_нового_столбца тип данных [NOT NULL]s

Попробуйте применить эту команду, добавив столбец к таблицеPLSQL101_LOG. Полученные результаты сравните с теми, что показаны нарис. 7.3.

DESC plsql!01_log

ALTER TABLE plsqll01_logADD data_load_date VARCHAR2(8);DESC plsql!01_log

230 Глава?

• .* Oracle SQL-Plus

File Edk Search Qptions tidpSQL> DESC plsqUei log

Name Null?

PURCHASE DATEPRODUCT NAMEPRODUCT PRICEqUANTITVFIRST NAMELftST_NAME

SQL>SQL> ALTER TABLE plsqlllH log

2 ADD data_load_date UARCHAR2(8) NULL;

Table altered.

SQL>SQL> DESC plsql101 log

Name Null?

PURCHASE DATEPRODUCT NAMEPRODUCT PRICEqUANTITVFIRST NAMELAST NAMEDATA_LOAD_DATE

SQL>

Hlil 13

Type — J

DATEUARCHAR2(25)NUMBER (it, 2)NUMBER(it,2)UARCHAR2(15)UARCHAR2(2Q)

Type

DATEUARCHftR2(25)NUMBER(lt,2)HUMBER(4,2)UARCHAR2(15)UflRCHftR2(2B)UARCHAR2(8)

^

Рис. 7.З. Добавление столбца к существующей таблице

Изменение типа данных столбцаВозможно, у вас возник вопрос: почему только что добавленный столбец яв-

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

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

I

ALTER TABLE имя_таблицыMODIFY имя_столбца новый_тип_данных

Попробуйте применить ее к таблице PLSQL101_LOG, введя следующийкод, и сравните результаты с показанными на рис. 7.4:

DESC plsqI101_log

ALTER TABLE plsql!01_log

MODIFY data_load_date DATE;

DESC plsql!01_log

Другие полезные средства Oracle 231

• * Uiacle SQL-Plus

File Edit Search Options Help

SQL> DESC plsq!101 logНале Null?

PURCHASE DATEPRODUCT NAMEPRODUCT PRICEQUANTITYFIRST NAMELAST NftMEDATA_LOAD_DATE

SQL>SQL> ALTER TABLE plsql1B1 log

2 MODIFY data_load_date DATE;

Table altered.

SQL>SQL> DESC plsql101 log

Name Null?

PURCHASE DATEPRODUCT NAMEPRODUCT PRICEQUANTITYFIRST NAMELAST NAMEDATA_LOAD_DATE

SQL>

jJJ

игап

Type

DATEUARCHAR2(25)HBHBER(4.2)NUMBER(ll,2)UARCHAR2(15)UARCHAR2(20)UARCHAR2(8)

' ' ' .

- -

Type

DATEUARCHAR2(25)NUM8ER(l|,2)HUMBER(it,2)UARCHAR2(15)UARCHAR2(20)DATE

^Рис. 7.4. Изменение типа данных существующего столбца

.Изменение null-опций

Часто во время разработки базы данных пользователи еще не знают, какиестолбцы будут обязательными, а какие — нет. В таких случаях обычно создают-ся столбцы, допускающие null-значения, а потом, при необходимости, их ста-тус меняется на противоположный. (Разумеется, с тем же успехом можнопревратить столбцы NOT NULL в NULL.) Для этого используется следующийсинтаксис:

ALTER TABLE имя_таблицыMODIFY имя_столбца NOT NULL

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

UPDATE plsql!01_log SET data_load_date = 45-DEC-2003';

DESC plsql!01_log

ALTER TABLE plsql!01_log MODIFY data_load_date NOT NULL;

DESC plsql!01_log

232 Глава 7

file Edit Search Options HelpSQL> UPDATE plsql101_log SET data_load_date - >15-ОЕС-2вВ31

7 rows updated.

SQL>

SQL> DESC plsql101 logName Null?

f>URCHASE_DATEPRODUCT_NAMEPRODUCTlPRICEQUANTITY

FIRST_HflMELAST_NflMEDATfl_LOAD_DATE

SQL>

SQL> ALTER TABLE plsqliei_log HODIFV data_load_date NOT NULL;

Table altered.

DATEUARCHAR2(2S)NUMBER(1»,2)

NUMBEHC.,2)

UARCHAR2(15)UARCHAR2(2D)DATE

SQL>SQL> DESC plsql101 logName Null? Type

PURCHASE_DATEPRODUCT_NAME

PRODUCT_PRICE

QUANTITY

FIRST_NAME

LAST_NAME

DATfl_LOAD_DATE

SQL> |

DATE

UARCHAR2(2S)

NUMBER(4,2)

NUMHER(U,2)

UARCHAR2(15)

UARCHAR2(2e)

NOT NULL DATE

Рис. 7.5. Изменение null-опции существующего столбца

ПредставленияИдея представления (view) проста: определить запрос, который предполага-

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

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

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

Другие полезные средства Oracle 233

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

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

Создание представленияМетод создания представления — это сама простота. Нужно указать только

имя представления и оператор SELECT, который будет выполняться при обра-щении к представлению. Вот соответствующий синтаксис:

CREATE OR REPLACE VIEW имя_представления ASоператор SELECTj

Обратите внимание, что здесь присутствует новый элемент: OR REPLACE.Он позволяет создавать новое представление даже тогда, когда представление суказанным именем уже существует. (Разумеется, существующее представлениепри этом перезаписывается.)

Чтобы увидеть, как работает представление, введите следующие команды исравните результаты с показанными на рис. 7.6:

SELECT * FROM plsql!01_purchase;

CREATE OR REPLACE VIEW plsql!01_sales_by_atlas_v AS

SELECT *

FROM plsql!01_purchase

WHEJ^E salesperson = 'CA'

SELECT * FROM plsqllOl sales by atlas v;_ _ j_ _

234 Глава 7

Eh £* Search Option» HelpSQL> SELECT » FROM plsql1U1_purchasf> ; _i

PRODUCTJMHE

Snail WidgetHediun WodgetChrome PhoobarSnail Widget

. Hediun WodgetRound Chrome SnaphooSnail Widget

7 rows selected.

SALESPERSON

CAOBGAGALBCACA

PURCHASE_

1*-JUL-831"1-JUL-0314-JUL-831S-JUL-0315-JUL-8316-JUL-0317-JUL-03

QUANTITV

175282051

SQL>SQL> CREATE OR REPLACE VIEW plsqll 01 sales by atlas и AS2 SELECT *Э FROM plsqll 01_purchaseu WHERE salesperson - *CA'5 ;

Uien created.

SQL>SQL> SELECT » FRON plsqll 01_sales_by_atlas_u;

PRODUCTJMHE

Snail WidgetRound Chrone SnaphooSnail Widget

SOL>

SALESPERSON

CACACA

' . :

PURCHASE_

11I-JUL-0316-JUL-0317-JUL-03

QUANTITV

15

: . 1 ;'i

Л

Рис. 7.6. Создание простого фильтрующего представления

Следующие команды показывают, как создать представление для просмот-ра данных из соединенных таблиц:

CREATE OR REPLACE VIEW plsql!01_sales_per_person_v AS'

SELECT pers.first_name I I ' ' I I pers.last_name SALESPERSON,purc.product_name,

purc.purchase_date,

pure.quantity

FROM plsqllOl^person pers,

plsql!01_purchase pure

pers.person_code = pure.salesperson (+)WHERE

;

SELECT * FROM plsql!01_sales_per_person_v

ORDER BY salesperson, product_name, purchase_date;

Обратите внимание, что в последнем примере конструкция ORDER BYвключена в оператор SELECT, извлекающий данные из представления, а не всамо представление. До появления Oracle 8/ представления не могли содержатьконструкцию ORDER BY. В 8/ и последующих версиях можно заставить пред-ставление сортировать отображаемые записи, указав сразу после конструкцииWHERE конструкцию ORDER BY, как в стандартном операторе SELECT.

Другие полезные средства Oracle 235

Удаление представленийУдалить представление так же легко, как и таблицу (но это действие менее

разрушительно, поскольку представление не содержит никаких данных; самоехудшее, к чему может привести случайное удаление представления,.—> это к не-обходимости создавать его заново). Команда, удаляющая представление, имеетследующий синтаксис:

DROP VIEW имя_представления;

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

DROP VIEW plsqll01_sales_per_person_v;

Изменение определения представленияOracle не позволяет изменять существующее представление. Единственный

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

Анализ первых/V записейУзнав, насколько легко с помощью SQL можно просмотреть первые 1,10

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

В данном методе используется тот факт, что каждой записи, возвращаемой врезультате обработки любого запроса, динамически присваивается порядко-вый номер. Первая (или единственная) возвращенная запись получит номер \независимо от своего положения в таблице. На эти номера можно ссылаться вконструкции WHERE. Если в операторе SELECT выполняется сортировка ре-зультатов, можно добиться того, что Oracle покажет'5, 50 или 500 наиболее важ-ных записей, дополнив оператор конструкцией WHERE, ограничивающейколичество выводимых строк.

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

SELECT имя_столбца_1 [, имя_столбца_2...]FROM имя_таблщыWHERE ROWNUM <= нужное_число_записейORDER BY столбец_сортировки

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

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

236 Глава?

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

ПримечаниеТакое представление будет работать только в версиях 81 и старше,поскольку эти версии позволяют включать в представленияконструкцию ORDER BY.

CREATE OR REPLACE VIEW plsq!101_overstocked_items AS

SELECT product_na!tie, quantity_on__handFROM plsql!01_product

WHERE ROWNUM <= 3

ORDER BY quant.ity_on_hand

SELECT * FROM plsqll01_overstocked_Items;

Другие объекты базы данныхВ последнем разделе этой главы рассматриваются различные средства Oracle,

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

ПоследовательностиБазы данных предназначены для поддержания порядка в массивах инфор-

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

Создание последовательностиДля создания последовательности используется следующий синтаксис:

CREATE SEQUENCE имя_последователъности',

Эта простая команда создает последовательность, которая начинается с 1 иувеличивается на 1 при каждом обращении. Ничего другого от последователь-ностей часто и не требуется. Тем не менее при определении последовательно-сти можно использовать много дополнительных параметров. Взгляните наприведенный ниже синтаксис, где перечислены наиболее полезные из них:

CREATE SEQUENCE имя_последовательности[INCREMENT BY значение_инкремента][START WITH начальное_значение][MAXVALUE наибольшее_значение][MINVALUE наименьшее_значение][CYCLE]

Другие полезные средстра Oracle 237

Параметр INCREMENT BY позволяет создавать последовательности с инк-рементом, отличным от 1. Значение этого параметра может содержать до 28цифр (хотя трудно представить ситуации, где может найти применение такойинкремент!). Если указать здесь отрицательное число, то значение последова-тельности будет уменьшаться при каждом обращении.

Параметр START WITH позволяет создать последовательность, начальноезначение которой отлично от 1 . Это может пригодиться при создании последо-вательности для таблицы, уже содержащей записи, чтобы начать последовате-льность с числа, следующего за наибольшим существующим идентификаторомзаписи.

Параметры MAXVALUE и MINVALUE позволяют ограничить интервал чи-сел, генерируемых последовательностью. Если использовать их в сочетании с па-раметром CYCLE, заданное множество значений будет циклически повторяться.

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

CREATE SEQUENCE plsqll01_test_seq;

Использование последовательностиЧтобы получать значения последовательности, на нее необходимо ссылать-

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

Чтобы увидеть, как это происходит, введите следующие команды и сравнитерезультаты с показанными на рис. 7.7:

SELECT plsql!01_test_seq.nextval FROM DUAL;

SELECT plsq!101_test_seq.nextval FROM DUAL;

SELECT plsq!101_test_seq.nextval FROM DUAL;

Теперь вам нужно научиться заполнять столбцы таблицы из последователь-ности. Это делается путем включения ссылки на последовательность в опера-тор INSERT, как в приведенных ниже командах. Введите их и сравнитерезультаты с показанными на рис. 7.8.

CREATE TABLE plsql!01_test (

record_id NUMBER (18, 0),

record_text VARCHAR2 (10)

);INSERT INTO plsql!01_test VALUES (

plsql!01_test_seq.nextval,

'Record A'

INSERT INTO plsql!01_test VALUES (

plsql!01_test_seq.nextval,

'Record B'

) ;SELECT * FROM plsq!101_test;

238 Глава?

Fie £d» Starch Sffaa HelpSQL> CREATE SEQUENCE plsql191_test_seq;

Sequence created.

SQL>SQL> SELECT plsqlin_test_seq.nextual FROM DUAL;

NEXTUAL

SQL> SELECT plsqliei_test_seq.nextual FROM DUAL;

NEXTUAL

2SQL> SELECT plsql1B1_test_seq.nextual FROM DUAL;

NEXTUAL

3

SQL> |

Рис. 7.7. Обращение к последовательности из командной строки

Efc Ed» Search Options HelpSQL> CREATE TABLE plsq!101_test (

2 record_id NUM8ER(18.B),3 record_text UflRCHAR2(1B)» ):

Table created.

SQL> INSERT INTO plsql1B1_test UALUES (2 plsq!101_test seq.nextval,

3 'Record A'

* );

1 row created.

SQL> INSERT INTO plsql1B1_test UALUES (2 plsql1t1_test seq.nextual,

3 'Record B'

* );

1 row created.

SQL> SELECT • FROM plsqlle1_test;

RECORD ID RECORD TEX

t Record A5 Record В

SQL>

^J

Рис. 7.8. Использование последовательности для заполнениястолбца таблицы

Другие полезные средства Oracle 239

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

В методе, который вы только что использовали, демонстрировалось обра-щение к последовательности при помощи явной ссылки в операторе INSERT.Можно также организовать автоматическое обращение к последовательности,чтобы не ссылаться на нее в операторе INSERT. Этот метод будет рассмотрен вглаве 9.

Модификация существующей последовательностиСозданную последовательность можно модифицировать различными спо-

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

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

ALTER SEQUENCE имя последовательности[INCREMENT BY значение_инкремента][MAXVALUE наибольшее-значение \ NOMAXVALUE][MINVALUE наименыиее__значение \ NOMINVALUE][CYCLE | NOCYCLE]5

В качестве примера введите следующие команды и сравните их результаты споказанными на рис. 7.9:

ALTER SEQUENCE plsql!01_test_seqMAXVALUE 10

SELECT plsqll01_test_seq.nextval FROM DUAL;SELECT plsq!101_test_seq.nextval FROM DUAL;

SELECT plsqll01_test_seq.nextval FROM DUAL;

SELECT plsql!01_test_seq.nextval FROM DUAL;

SELECT plsql!01_test_seq.nextval FROM DUAL;

SELECT plsql!01_test_seq.nextval FROM DUAL;

СинонимыСиноним (synonym) позволяет ссылаться на объект Oracle по имени, которое

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

9 Зак. 725

240 Глава?

SQL> ALTER SEQUENCE plsql1S1 test_seq2 MftXUflLUE 118 ;

Sequence altered.

SQL>SQL> SELECT plsqlH1_test.seq.nextval FROH OUflL;

NEXTUflL

SQL> SELECT plsql1«_test_seq.nextual FROH DUflL;

KEXTUAL

7

SQL> SELECT plsql101_test_seq.nextwal FROH DUflL;

HEXTUflL' ________ __

8 , > .

SQL> SELECT plsql1B1_test_seq.nextual FROH DUflL;

NEXTUflL

9

SQL> SELECT plsql1B1_test_seq.nextual FROM DUflL;

NEXTUflL

11

SQL> SELECT plsql1Q1 test seq.nextual FROH DUflL;

SELECT plsql1B1_test_seq.nextual FROH DUAL

*

ERROR at line 1:ORfl-MBM: sequence PLSQL1*1_TEST_SEQ. NEXTUflL exceeds HAXUALUE and cannot

SQL> f

be i

Рис. 7.9. Изменение существующей последовательности

Зачем нужно создавать синоним для какого-либо объекта? Главным обра-зом для удобства: если вы часто ссылаетесь на таблицу с длинным именем, то подостоинству оцените возможность использования короткого имени без пере-именования таблицы и изменения кода, который на нее ссылается.

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

SELECT * РКОМваше_имя_пользователя.имя_вашей_табл1щы;

Другие полезные средства Oracle 241

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

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

CREATE [PUPLIC] SYNONYM имя_синонимаFOR имя объекта

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

SELECT * FROM prod;

CREATE SYNONYM prod FOR plsql!01_product;

SELECT * FROM prod;

file Edit Search Qptbns Цг1р •SQL> SELECT « FROH prod;SELECT > FROM prod

ERROR at line 1:ORA-00942: table or vie* does not exist

SQL>SQL> СВЕЙТЕ SVNONVH prod FOR plsql101_product; .

Synonyn created.

SQL>SQL> SELECT * FROH prod;

PRODUCTJttHE PRODUCT_PRICE QUAHTITY_ON_HftNO LftST_STOC

Snail WidgetMedium WodgetChrome PhoobarRound Chrome SnaphooExtra Huge Mega Phoobar *Square ZinculatorAnodized FranifierRed SnaphooBlue Snaphoo

9 rows selected.

SQL>

oLJ

99755125

1.9641.5

1.761.76

15-JAN-0315-JAH-02

110 15-JAN-B31MH1234 15-JAH-04

1 31-DEC-02511 31-DEC-B1II 38-DEC-01

Л

Рис. 7.10. Создание синонима для таблицы; • • - - • - . .

9*

242 Глава?

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

CREATE PUBLIC SYNONYM plsqll01_prqduct FOR plsqll01_product;

Модификация существующего синонимаВвиду чрезвычайной простоты синонимов Oracle не предоставляет никаких

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

DROP [PUBLIC] SYNONYM имя_синонима;

Чтобы удалить первый из созданных вы ше синонимов (PROD), введите сле-дующую команду:

DROP SYNONYM prod;

Для удаления общего синонима введите такую команду:

DROP PUBLIC SYNONYM plsql!01_product;'

Словарь данных OracleВероятно, вы уже поняли, что база данных Oracle состоит из множества раз-

личных объектов: таблиц, столбцов, представлений, связей, ограничений, по-следовательностей и т.д. Чтобы следить за всеми этими объектами, Oracleсохраняет информацию о них в словаре данных (data dictionary). Словарьданныхпредставляет собой набор таблиц и представлений, содержащих самую послед-нюю информацию о каждом объекте и пользователе базы данных. Он содержиткаждую характеристику, указанную вами при создании объекта, а также слу-жебную информацию — в частности, размер пространства, выделенного объ-екту, размер используемого пространства и права пользователей, относящиесяк этому объекту.

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

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

SELECT table_name, SUBSTR(comments, 1, 45)FROM diet

WHERE SUBSTR(comments, 1, 7) <> 'Synonym'

f

Использование различных представленийсловаря данныхВ Oracle существует так много различных представлений словаря данных,

что об их использовании можно написать отдельную книгу (во многих книгах,

Другие полезные средства Oracle 243

Jt Oiacle SQL'PlusFile Edit Search Options Help

USER_REPPRIORITV_GROUPUSER_REPPROPUSER_REPRESuLUTIONUSER_REPRESOLUTION_METHODUSER_REPRESOLUTION STflTISTICSUSER_REPRESOL_STATS_CONTROLUSER_REPSCHEMAUSER_REPSITESUSER_RESOURCE_LIMITSUSER_ROLE_PRIUSUSER_SEGMENTSUSEH_SEQUEHCESUSER_SNflPSHOTSUSER_SNflPSHOT_LOGSUSER_SHftPSHOT_REFRESH_TIMESUSER_SOURCEUSER_SYNONYMSUSER_SVS_PRIUSUSER_TftBLESUSERJTABLESPACESUSER_TAB_COLUNNSUSER_TAB_COL_STATISTICSUSER_TBB_COHMEHTSUSER_TfiB_HISTBGRflMSUSER_TflB_PRIUSUSEB_rBB_PRIUS_MADEUSER_TflB_PBIUS_RECDUSERJTRIGGERSUSER_TRIGGER_COLSUSER_TS_QUOTflSUSER_TVPESUSER_TVPE_flTTRSUSER_TVPE_METHODSUSER_UPDATABLE_COLUI1NSUSERJJSERSUSERJJIEWSflUOIT_flCTIONSCOLUMN_PRIUILEGESDICTIONflRVDICT_COLUMNSGLOBAL NAMEINDEXjUSTOGHIWINDEX_STATSHLS_DATABflSE_PnRflMETERSNLS_INSTANCE_PARAMETERSHLS_SESSION_PARAMETERSRESOURCE_COSTROLE_ROLE_PRIUSROLE_SYS PRIUSRDLE_TAB~PRIUSSESSION_PRIUSSESS10N_ROLESTABLE PRIUILEGES

286 rows selected.

SQL>

mmm\

Information about user's priority groupsPropagation information about the current use

Description of all conflict resolutions for uAll conflict resolution methods accessible toStatistics for conflict resolutions for user"Information about statistics collection for сN-way replication information about the curre

K-uay replication information about the curreDisplay resource limit of the user

Roles granted to current userStorage allocated for all database segments

Description of the user's own SEQUENCESSnapshots the user can look at

All snapshot logs owned by the userSnapshots and their last refresh tines for eaSource of stored objects accessible to the us

The user's priuate synonymsSystem privileges granted to current user

Description of the user's own relational tablDescription of accessible tablespacesColumns of user's tables, views and clustersColumns of user's tables, uiews and clustersComments on the tables and uiews owned by the

Histograms on columns of user's tablesGrants on objects for which the user is the оAll grants on objects owned by the userGrants on objects for which the user is the gTriggers owned by the userColumn usage in user's triggersTablespace quotas for the userDescription of the user's own typesDescription of attributes of the user's own tDescription of methods of the user's own typeDescription of updatable columns

Information about the current userDescription of the user's own uiewsDescription table for audit trail action typeGrants on columns for which the user is the gDescription of data dictionary tables and uieDescription of columns in data dictionary tabglobal database namestatistics on keys with repeat countstatistics on the b-tree

Permanent NLS parameters of the databaseNLS parameters of the instanceNLS parameters of the user sessionCost for each resourceRoles which are granted to rolesSystem privileges granted to rolesTable privileges granted to rolesPrivileges which the user currently has setRoles which the user currently has enabled.Grants on objects for which the user is the g

Рис. 7.11. Просмотр объектов в словаре данных Oracle

244 Глава?

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

SELECT table_name FROM user_tables;

А эта команда выведет список всех созданных вами представлений:

SELECT view_name FROM user_views;

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

ИтогиВ этой главе были описаны различные средства, знакомство с которыми

расширило ваши знания об SQL. В начале главы было показано, как перено-сить данные между таблицами с использованием оператора INSERT, содержа-щего вложенный оператор SELECT в том месте, где обычно указываютсявставляемые значения. Данные можно переносить и с помощью оператораCREATE TABLE, в котором определения столбцов заменены на операторSELECT.

Научившись переименовывать таблицы с использованием простого синтак-сиса RENAME имя_старой_таблицы ТО имя_новой_таблицы, вы узнали, какизменять структуру таблицы путем добавления новых столбцов, а также изме-нения типа данных и null-опций существующих столбцов. Затем вы познако-мились с представлениями, которые, по существу, представляют собойхранимые запросы. Представления позволяют писать команды для полученияопределенных результатов из одной или нескольких таблиц и сохранять этикоманды для последующего использования. В качестве примера вы создалипредставление, которое показывало товары, запас которых на складе наиболеевелик.

Далее вы узнали о последовательностях — механизме Oracle, обеспечиваю-щем генерацию последовательных номеров для использования в качествеидентификаторов записей и в других операциях подсчета. Команда CREATESEQUENCE позволяет указать такие параметры последовательности, как на-чальное, наименьшее и наибольшее значение, величину инкремента, а такжеразрешить или запретить циклический повтор по достижении граничного зна-чения. Значения последовательности можно включать в оператор INSERT,указывая ссылку вида непоследовательности, nextval.

Следующее, с чем вы познакомились, — это синонимы, позволяющие ссы-латься на объекты Oracle по любому, а не только фактическому, имени. Испо-льзуя синонимы, можно предоставлять доступ к таблицам и другим объектамвсем пользователям базы данных, не требуя от них знания имени владельцаобъекта.

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

Другие полезные средства Oracle 245

темы вы узнали, как получить список всех представлений словаря данных. Вытакже научились как получать списки своих собственных таблиц и представле-ний, делая выборку из представлений словаря данных USER_TABLES иUSER_VIEWS.

Эта и предшествующие главы содержали все основные сведения, необходи-мые для эффективной работы с SQL. Теперь вы полностью готовы к тому, что-бы учиться писать сложные программы с использованием PL/SQL —супермножества SQL, предлагаемого Oracle. На современном рынке труда уме-ние писать программы на PL/SQL дает такую же уверенность в завтрашнем дне,как и счет в банке.

Вопросы1 . Какая из следующих команд перенесет данные из таблицы PRODUCT p

таблицу PRODUCT.ARCHIVE?

A. INSERT INTO product (SELECT *FROM product_archive

B. COPY * FROM product TO product_archive;

C. CREATE TABLE product_archive ASSELECT * FROM product;

D. INSERT INTO product_archive (SELECT * FROM product);' i . • •'.".; \ • ! . ' • • •

2. Какая из следующих команд переименует таблицу?

A. RENAME имя_таблицы новое_имя_таблицы;

B. RENAME имя_таблицы ТО новое_имя_таблицы;

C. RENAME TABLE имя_таблицы новое_имя_таблицы;

D. RENAME TABLE имя_таблицы ТО новое_имя_таблицы\

3. Какая из следующих команд добавит обязательный текстовый столбецNEW_COLUMN к таблице ТАВ1?

A. ALTER TABLE tab 1ADD new_column VARCHAR2(10) NOT NULL;

B. ADD new_column VARCHAR2(10) NOT NULL TO tabl;

C. ALTER tablADD new_column VARCHAR2(10) NOT NULL;

D. ADD new_column VARCHAR2(10) NOT NULL TO TABLE tabl;

4. Что из перечисленного не относится к достоинствам представлений?

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

246 Глава?

B. Возможность соединять информацию из многих таблиц.

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

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

5. Какая из следующих команд не приведет к созданию последовательности?

A. CREATE SEQUENCE new_seql NOMAXVALUE;

B. CREATE SEQUENCE 2new_seq START WITH 2;

C. CREATE SEQUENCE new3_seq MIN 1 MAX 100 CYCLE;

D. CREATE SEQUENCE new_4seq INCREMENT BY -1;

6. Что из перечисленного относится к достоинствам синонимов?

A. Увеличение скорости передачи данных.

B. Возможность ссылаться на столбец по другому имени.

C. Возможность ссылаться на таблицу по другому имени.

D. Возможность ссылаться на таблицу, не зная ее владельца.

Ответы на вопросы1. С, D. CREATE TABLE product_archive AS

SELECT * FROM product;INSERT INTO product_archive (SELECT * FROM product); ;

Объяснение Вариант А имеет правильный синтаксис, нотаблица-источник и таблица назначения перепутаны местами.Вариант В синтаксически неверен. Варианты С и D показываютправильные способы копирования данных из PRODUCT вPRODUCT_ARCHIVE.

,2. В. RENAME имя_таблицы ТО новое_имя_таблицы;

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

3. A. ALTER TABLE tab 1ADD new_column VARCHAR2(10) NOT NULL;

Объяснение Обратитесь к разделу "Добавление столбцов", чтобыосвежить в памяти синтаксис команды ALTER TABLE.

4. D. Ускорение доступа к данным за счет прямого обращения кнеобходимым столбцам

Объяснение В варианте D говорится о выигрыше, который даютиндексы, а не представления. Использование представления неоказывает существенного влияния на скорость доступа к данным.

Другие полезные средства Oracle 247

5. В, С. CREATE SEQUENCE 2new_seq START WITH 2;CREATE SEQUENCE new3_seq MIN 1 MAX 100 CYCLE;

Объяснение Команда В не будет выполнена, поскольку имяпоследовательности начинается с цифры (вспомните правилаименования объектов). Команда С не будет выполнена из-за того, чтопараметры для установки предельных значений называютсяMINVALUE и MAXVALUE, а не MIN и МАХ. Если вы подумали, чтовариант D тоже ошибочен из-за отрицательного значения инкремента,вспомните, что именно таким способом создается последовательностьс уменьшающимися значениями.

6. С, D. Возможность ссылаться на таблицу по другому имени,возможность ссылаться на таблицу, не зная ее владельца

Объяснение Обратитесь к разделу "Синонимы", чтобы освежить впамяти эту тему.

Часть

IIIСоздание программ

на PL/SQL

.

Глава

'

Введение в PL/SQL

252 Глава 8

Сохранение и поиск информации — это лишь часть функций любого реально-го приложения. Даже в простейшем приложении требуется выполнять обра-ботку данных, которую трудно или невозможно реализовать с использованиемодного лишь SQL. Только подумайте, насколько сложен ежегодный расчет ва-ших налогов! Примеров привести можно много. В любом случае, одного SQLоказывается недостаточно.

Что такое PL/SQL?Вы можете спросить, почему в SQL отсутствуют средства для более сложных

вычислений с данными. Причина этого отчасти историческая: SQL создавалсякак язык запросов к базам данных (Structured Query Language, язык структури-рованных запросов), поэтому его развитие шло по пути оптимизации под един-ственную задачу — выполнение запросов. Различные поставщикипрограммного обеспечения баз выработали общие стандарты SQL, но не дого-ворились о том, как предоставить пользователям бол ее сложные SQL-ориенти-рованные программные средства. В результате каждый поставщик СУБДпредлагает оригинальные или частично стандартизованные продукты. Корпо-рация Oracle называет свое решение PL/SQL. Можете считать это сокращени-ем от "Programming Language for SQL".

Эта глава представляет собой введение в PL/SQL. Вы узнаете о различияхмежду SQL, 'PL/SQL и SQL*Plus, а также научитесь писать простыеPL/SQL-процедуры и функции, используя базовые элементы PL/SQL — пере-менные, циклы и курсоры. Затем вы познакомитесь с искусством обрабатыватьошибки таким способом, который может быть с легкостью понят пользовате-лями.

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

DROP TABLE plsqllOljourchase;

DROP TABLE plsql!01_product;

DROP TABLE plsqllOljoerson;

DROP TABLE plsqll01_old_item;

DROP TABLE plsql!01_purchase_archive;

CREATE TABLE plsql!01_person (

person_code VARCHAR2P) PRIMARY KEY,

first_name VARCHAR2 (15) ,las t_name VARCHAR2 (20),

hire_date DATE

CREATE INDEX plsqll01_person_name_index

ON plsql!01_person(last_name, first_name) ;

ALTER TABLE plsql!01_person

ADD CONSTRAINT plsql!01_person_unique UNIQUE (

first_name,

Введение в PL/SQL 253

last_name,

hiredate

INSERT INTO plsql!01_person VALUES

CCA', 'Charlene', 'Atlas', 'Ol-FEB-02');

INSERT INTO plsql!01_person VALUES

('GA', 'Gary1, 'Anderson', 45-FEB-02.') ;

INSERT INTO plsql!01_person VALUES

CBB', 'Bobby', 'Barkenhagen1, '28-FEB-02

1);

INSERT INTO plsql!01_person VALUES('LB', 'Laren', 'Baxter

1, 'Ol-MAR-02');

INSERT INTO plsql!01_person VALUES {

'LN', 'Linda',~ 'Norton', 'Ol-JUN-03');

CREATE TABLE plsql!01_product (

product_name VARCHAR2(25) PRIMARY KEY,

product_price NUMBER(4,2),

quantity_on_hand NUMBER(5,0),

laststockdate DATE

ALTER TABLE plsql!01_product ADD CONSTRAINT positive_quantity 'CHECK(

quantity_on_hand IS NOT NULL

ANDquantity_on_hand >= 0

INSERT INTO plsql!01_product VALUES ;'•••/;

('Small Widget', 99, 1, ' 15-JAN-03 ' ) ;

INSERT INTO plsql!01_product VALUES( 'Medium Wodget' , 75, 1000, ' 15-JAN-02 ' ) ;

INSERT INTO plsql!01_product VALUES('Chrome Phoobar

1, 50, 100, ' 15-JAN-03' ) ;

INSERT INTO plsql!01_product VALUES

('Round Chrome Snaphoo1, 25, 10000, null);

INSERT INTO plsql!01_product VALUES(•Extra Huge Mega Phoobar +', 9.95, 1234, ' 15-JAN-04 ' ) ;

INSERT INTO plsql!01_product VALUES ('Square Zinculator1,

45, 1, TO_DATE (' December 31, 2002, 11:30 P.M.',

'Month dd, YYYY, HH:MI P.M.')

INSERT INT6 plsql!01_product VALUES (

•Anodized Framifier1, 49, 5, NULL) ;

INSERT INTO plsql!01_product VALUES (1 Red Snaphoo ', 1.95, 10, ' 31-DEC-01 ' ) ;

INSERT INTO plsql!01_product VALUES ('Blue Snaphoo', 1.95, 10, '30-DEC-01')

254 Глава 8

CREATE TABLE plsql!01_purchase (product_name VARCHAR2(25),salesperson VARCHAR2(3),purchase_date DATE,quantity NUMBER(4,2)

ALTER TABLE plsql!01_purchase

ADD PRIMARY KEY (product_name,salesperson,

purchase_date

ALTER TABLE plsql!01_purchase ADD CONSTRAINT reasonable_date CHECK I

purchase_date IS NOT NULLANDTO_CHAR(purchase_date, ' YYYY-MM-DD

1 ) >- '2000-06-30'

ALTER TABLE plsql!01_purchaseADD CONSTRAINT plsql!01_purchase_f k_product FOREIGN KEY(product_name) REFERENCES plsql!01_product;

ALTER TABLE plsql!01_purchase

ADD CONSTRAINT plsql!01_purchase_f k_person FOREIGN KEY(salesperson) REFERENCES plsql!01_person;

CREATE INDEX plsql!01_purchase_productON plsql!01_purchase (product_name) ;CREATE INDEX plsql!01_purchase_salesperson

ON plsql!01_purchase (salesperson) ;

INSERT INTO plsql!01_purchase VALUES

('Small Widget', 'CA1, 44-JUL-03', 1);

INSERT INTO plsqllOljpurchase VALUES -('Medium Wodget ' , 'BB

1, '14-JUL-03', 75);

INSERT INTO plsql!01_purchase VALUES('Chrome Phoobar', 'GA', 44-JUL-03

1, 2);

INSERT INTO plsq!101_purchase VALUES('Small Widget

1, 'GA', ' 15-JUL-03 ' , 8) ;

INSERT INTO plsq!101_purchase VALUES( 'Medium Wodget' , 'LB', '15-JUL-03', 20);

INSERT INTO plsql!01_purchase VALUES

('Round Chrome Snaphoo ' , 'CA', 46-JUL-03', 5) ;INSERT INTO plsql!01_purchase VALUES (

'Small Widget1, 'CA', '17-JUL-03

1, 1)

UPDATE plsqll.01_productSET product_price = product_price * .9

Введение в PL/SQL 255

WHERE product_name NOT IN (SELECT DISTINCT product_nameFROM plsql!01_purchase

CREATE TABLE plsq!101_old_item (item_id CHAR(20),item_desc CHAR(25)

plsql!01_old_item VALUES

101', 'Can, Small1);

plsq!101_old_item VALUES102', 'Can, Large');plsq!101_old_item VALUES103', 'Bottle, Small');

plsqll01_old_item VALUES104', 'Bottle, Large');plsqll01_old_item VALUES

101', •Box, Small');plsq!101_old_item VALUES

102', 'Box, Large');plsq!101_old_item VALUES

103', 'Shipping Carton, Small1);

plsq!101_old_item VALUES104', 'Shipping Carton, Large');

CREATE TABLE plsql!01_purchase_archive (product_name VARCHAR2(25),salesperson VARCHAR2(3),

purchase_date DATE,quantity NUMBER(4,2)

INSERT

INSERT

INSERT

INSERT

INSERT

INSERT

INSERT

INSERT

INTOCLA-INTO('LA-

INTO( 'LA-INTO

CLA-INTO

CNY-INTO( 'NY-INTOCNY-INTO('NY-

INSERT INTO plsql!01_purchase_archive VALUES('Round Snaphoo', 'BB', '21-JUN-01', 10);

INSERT INTO plsq!101_purchase_archive VALUES('Large Harflinger', 'GA

1, '22-JUN-01

1, 50);

INSERT INTO plsql!01_purchase_archive VALUES('Medium Wodget', 'LB', '23-JUN-01', 20);

INSERT INTO plsql!01_purchase_archive VALUES('Small Widget', 'ZZ

1, '24-JUN-02', 80);

INSERT INTO plsql!01_purchase_archive VALUES('Chrome Phoobar', 'CA', '25-JUN-02', 2);

INSERT INTO plsql!01_purchase_archive VALUES

('Small Widget1, 'JT', '26-JUN-02', 50);

Общие сведения о PL/SQLPL/SQL предоставляет средства, позволяющие выполнять сложную обра-

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

256 Глава8

сводную таблицу — пакеты (packages) PL/SQL помогут это сделать. Вам нужносвоевременно узнавать о поступлении крупных заказов, чтобы привлекать дляих обслуживания дополнительных поставщиков, — PL/SQL предоставляеттриггеры (triggers), выдающие уведомление, как только объем сделанного зака-за превысит установленный вами предел. Вы можете использовать хранимыепроцедуры (stored procedures) PL/SQL для определения эффективности работыслужащих, чтобы на основе этих данных принимать решение о выплате пре-мий. Элегантные функции PL/SQL могут рассчитывать налоговые вычеты дляслужащих.

PL/SQL позволяет использовать все команды манипулирования данными,управления курсорами и транзакциями, присутствующие в SQL, а также всеSQL-функции и операторы. За счет этого вы можете гибко и безопасно мани-пулировать данными Oracle. Кроме того, PL/SQL полностью поддерживаеттипы данных SQL, что уменьшает количество преобразований типов при пе-редаче информации между приложениями и базой данных. PL/SQL такжеподдерживает динамический SQL — усовершенствованную программнуютехнологию, позволяющую делать приложения более гибкими и универсаль-ными. Ваши программы могут создавать и обрабатывать SQL-операторыопределения данных, управления данными и управления сеансами "на лету",во время выполнения.

Перед тем как переходить к изучению некоторых из этих мощных средств,хочу пояснить, как соотносятся друг с другом PL/SQL, SQL и SQL*Plus.

SQL, PL/SQL и SQL*Plus: кто есть ктоПредставьте себе ресторан. Вы входите внутрь, и вас (будем надеяться) уже

ждет хорошо обученный официант. Вы пробегаете глазами меню и делаете за-каз. Официант записывает заказ и отдает его на кухню. На огромной кухне тру-дятся множество поваров и помощников. Здесь хранится много еды —приготовленной, частично приготовленной и неприготовленной. У каждого изработающих на кухне свои обязанности: кто-то переносит продукты между хо-лодильником и рабочими столами, кто-то готовит блюда определенного типа(например, только супы или только салаты) и т.д. В зависимости от выбранныхпунктов меню официант делит заказ между разными поварам. Простые заказымогут выполняться одним поваром, тогда как более сложные потребуют при-влечения помощников или даже участия нескольких пйваров. Кроме того, не-которые заказываемые блюда являются стандартными (официанту достаточносказать повару: "Пицца с грибами"), а другие составляются самими клиентамии должны сопровождаться подробным списком ингредиентов.

Теперь немного изменим этот сценарий. Представим, что база данныхOracle — это кухня ресторана, a SQL*Plus — официант, передающий наши за-казы (сценарии, команды или программы) на кухню, т.е. в базу данных. На"кухне" есть два главных "повара": SQL и PL/SQL. Подобно официанту,SQL*Plus знает, какие заказы он может обработать сам, а какие должен отдатьопределенному повару. Точно так же, как официант может принести вам ста-кан воды, не привлекая к этому делу повара, SQL*Plus может отрегулироватьширину строк, отображаемых на экране, не обращаясь к базе данных.

Введение в PL/SQL 257

Команды или программы, которые вы вводите в командной строкеSQL*Plus, в некотором смысле аналогичны специально заказанной пицце. Надкаждым индивидуальным заказом повар должен немного подумать. Точно также, как повар держит рецепт пиццы с сыром в голове, PL/SQL может хранить"рецепты" ваших излюбленных заказов. Эти хранимые элементы PL/SQL на-зываются триггерами, хранимыми функциями, хранимыми процедурами и па-кетами. Скоро вы узнаете о них подробнее.

Какупоминалосьвыше, некоторые заказы должны готовиться более чем од-ним поваром. В большинстве интересных и полезных приложений баз данных,которые вам предстоит создавать, SQL и PL/SQL будут работать вместе, обме-ниваясь информацией в процессе выполнения сценария или программы. В ре-сторане приготовленный заказ передается официанту, чтобы тот доставил егона ваш стол. Подобно этому, после обработки команд SQL и PL/SQL передаютрезультаты SQL*Plus (или специализированной интерфейсной форме) для по-каза пользователю.

Хранимые процедуры, функции и триггерыПроцедуры, функции и триггеры PL/SQL помогают легко реализовывать

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

Хранимые процедурыХранимая процедура — это определенный набор инструкций, написанных на

языке PL/SQL. Вызов процедуры приводит к выполнению содержащихся вней инструкций. Процедура хранится в базе данных, поэтому и называетсяхранимой.

Хранимая процедура может выполнять SQL-операторы и манипулироватьданными в таблицах. Ее можно вызывать из другой хранимой процедурыPL/SQL, хранимой функции или триггера, а также непосредственно из строкиприглашения SQL*Plus. По мере чтения главы вы научитесь использовать всеперечисленные методы вызова.

Процедура состоит из двух основных частей: спецификации и тела. Специ-фикация процедуры (procedure specification) включает в себя имя процедуры иописание ее входных и выходных данных. Эти входные и выходные данные на-зываются формальными параметрами (formal parameters) или формальными аргу-ментами (formal arguments). Если при вызове процедуры указываютсяпараметры командной строки или другие входные данные, эти значения назы-ваются фактическими (actual) параметрами или фактическими аргументами.

Теперь рассмотрим некоторые примеры спецификаций процедур. (Помни-те, что спецификация не содержит никакого кода; в ней определяется толькоимя процедуры, а также ее входные и выходные параметры.)

rtm_ytd_reports

Эта простая спецификация содержит только имя процедуры. Данная про-цедура не имеет параметров.

258 Глава 8

\' •..,-... increase_prices (percent_increase NUMBER)

Этой процедуре при вызове может быть передано значение. Внутри процедурызначение будет известно под именем PERCENT_INCREASE. Обратите внима-ние, что здесь указан тип данных: NUMBER.

\ ' increase_salary_find_tax (increase_percent IN NUMBER := 7,sal IN OUT NUMBER,

tax OUT NUMBER

)

Здесь мы видим процедуру с тремя формальными параметрами. Слово INпосле имени параметра означает, что при вызове процедура может считать изэтого параметра входное значение. Слово OUT означает, что процедура можетиспользовать данный параметр для возврата значения в ту программу, из кото-рой она была вызвана. Комбинация IN OUT после имени параметра говорит отом, что параметр может использоваться как для передачи значения процедуре,так и для возврата значения.

Параметру INCREASE_PERCENT в этом примере присвоено значение поумолчанию (default value), равное 7, путем добавления := 7 после типа данных.Таким образом, если процедура будет вызвана без указания процента прироста,она увеличит переданное значение зарплаты на 7% и рассчитает налог, исходяиз новой зарплаты.

УШ ПримечаниеТипы данных в процедуре не могут иметь спецификаций размера.Например, вы можете указать для параметра тип данных NUMBER,HOHeNUMBER(10,2).

Тело процедуры (procedure body) — это блок PL/SQL-кода. О том, что пред-ставляет собой блок PL/SQL, вы узнаете, познакомившись со следующим раз-делом этой главы.

Хранимые функцииФункция PL/SQL похожа на процедуру PL/SQL: она также имеет специфи-

кацию и тело. Главное различие между процедурой и функцией в том, что фун-кция предназначена для возврата значения, которое может использоваться вболее крупном SQL-Операторе.

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

calcjpercent (value_l NUMBER,value_2 NUMBER) return NUMBER

Эта функция принимает в качестве входных параметров два числа, ссылаясьна них внутри себя как на VALUE_1 и VALUE_2. После написания тела функ-ции ее можно вызывать в SQL-операторе следующим образом:

INSERT INTO employee VALUES (3000, CALC PERCENT(300, 3000));

Введение в PL/SQL 259

ТриггерыТриггер — это процедура PL/SQL, которая выполняется автоматически, ког-

да происходит некоторое заданное событие, называемое триггерным событием(triggering event). Например, можно писать триггеры, срабатывающие при вы-полнении над таблицей операций INSERT, UPDATE или DELETE; при выда-че команд DDL; при входе пользователя в систему или его выходе из системы;при запуске или останове базы данных; при возникновении ошибок.

Между триггерами и процедурами PL/SQL есть три различия:

• Триггеры нельзя вызывать из кода программы. Oracle вызывает ихавтоматически в ответ на определенное событие.

• Триггеры не имеют списка параметров.

• Спецификация триггера немного отличается от спецификациипроцедуры.

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

Хранимые процедуры в сравнениис SQL-сценариями

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

'

Структура блока PL/SQLВ этом разделе вы познакомитесь с базовым блоком (basic block) PL/SQL.

Весь код PL/SQL, выполняющий фактическую работу, состоитиз базовых бло-ков. Изучив базовые блоки, можно рассматривать законченные примеры про-цедур, функций и триггеров.

Базовый блок PL/SQL состоит из четырех секций: секции заголовка (headersection), необязательной секции объявлений (declaration section), выполняемойсекции (execution section) и необязательной секции исключений (exception section).

Анонимный блок (anonumous block) — это блок PL/SQL без секции заголовка,иначе говоря, секции имени, поэтому он и называется анонимным. Аноним-ные блоки могут выполняться из SQL*Plus и использоваться в функциях, про-цедурах и триггерах PL/SQL. Вспомните, что сами процедуры, функции итриггеры также состоят из базовых блоков. Это означает, что базовый блокможно помещать в другой базовый блок. Чуть ниже вы узнаете об этом болееподробно.

По-видимому, лучший способ понять, что представляет собой базовыйблок, — это рассмотреть конкретный пример. Сначала введите команду, котораяпозволит просматривать в SQL*Plus информацию, выводимую программами:

*•*set serveroutput on

260 Глава 8

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

DECLARENum_a NUMBER := 6;Num_b NUMBER;

BEGINNumjb := 0;Num_a := Num_a / Num_b ;Num_b := 7;dbms_output .put_line ( ' Value of Num_b ' || Num_b) ;

EXCEPTIONWHEN ZERO_DIVIDE

THENdbms_output.put_line ( 'Trying to divide by zero');dbms_output.put_line ( ' Value of Num_a ' I I Num_a) ;dbms_output.put_line ( ' Value of Num_b ' || Num_b) ;

END;

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

компоненту программы принадлежит блок. Вспомните, что процедуры, функ-

A Oracle SQL-Plus ИВЕfile Edit Search £ptions це|р

SQL> set serueroutput onSQL> DECLflRE

2 nun_a NUMBER := 6;nun b NUMBER;

BEGINnun b

nun_a / num_b;nun_b - 7;dbns_output.put_line( ' Ualue of nun_b ' || num_b) ;

EXCEPTIONWHEN 2ERO_DIUIDETHENdbms_output.put_line(' Trying to diuide by zero

1);

345678910111213141516

Trying to divide by zeroUalue of nun_a бUalue of nun_b 0

PL/SQL procedure successfully completed.

SQL> |

_ _dbns_output.put_line(' Ualue of nun_adbms_output.put_line( ' Ualue of nun_b

END;

nun_a);nun_b);

тРис. 8.1. Пример анонимного блока PL/SQL

Введение в PL/SQL 261

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

«just_a_label»DECLARE

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

Секция объявленийСекция объявлений не является обязательной. В случае использования она

начинается после секции заголовка и оканчивается перед ключевым, словомBEGIN. Эта секция содержит объявления переменных, констант, курсоров,исключений, функций и процедур PL/SQL, которые будут использоваться ввыполняемой секции и секции исключений. Все объявления переменных иконстант должны размещаться до объявлений функций или процедур. О пере-менных и константах PL/SQL будет подробно рассказано в следующем разделе.Объявление сообщает PL/SQL о том, что нужно создать переменную, констан-ту, курсор, функцию или процедуру согласно приведенной спецификации.

Секция объявлений в примере, показанном на рис. 8.1 сообщает PL/SQL,что нужно создать две числовые переменные с именами Num_a и Num_b, при-своив переменной Num_a значение по умолчанию, равное 6.

Когда выполнение базового блока завершается, все элементы, объявленные всекции объявлений, перестают существовать. Элементы, объявленные в секцииобъявлений базового блока, могут использоваться только в пределах этого блока.Таким образом, после выполнения нашего демонстрационного блока вSQL*Plus переменную Num_a будет невозможно передать другой процедуреPL/SQL. Num_a и Num_b после выполнения блока просто исчезают. Но если извыполняемой секции блока будет вызываться функция или процедура PL/SQL,то Num_a и Num_b можно передать в качестве фактических параметров.

Одним словом, все, что находится в секции объявлений, при надлежит блокуи может использоваться только внутри него, а следовательно, существует толь-ко на протяжении его времени жизни. Часть кода, в которой может использова-ться переменная, называется областью видимости (scope). Областьювидимости переменных Num_a и Num_b является блок, в котором они объяв-лены. Эта область видимости простирается от начала секции объявлений и доконца выполняемой секции.

Выполняемая секцияВыполняемая секция начинается с ключевого слова BEGIN и заканчивает-

ся либо ключевым словом EXCEPTION, если присутствует секция исключе-

262 Глава 8

ний, либо ключевым словом END, за которым следуют необязательное имяфункции или процедуры и точка с запятой. Выполняемая секция содержитодин и более PL/SQL-операторов, выполняемых при передаче управления дан-ному блоку. Структура выполняемой секции показана ниже.

BEGINодин и более PL/'SQL-операторов

[секция исключений]END [имя функции или процедуры}',

В выполняемом коде PL/SQL чаще всего встречается оператор присваива-ния (:=). Он указывает, что нужно вычислить выражение справа и поместитьрезультат в переменную слева. Выполняемая секция нашего демонстрацион-ного блока содержит три оператора присваивания. Первый оператор присваи-вает переменной Num_b нулевое значение.

Второй оператор присваивает переменной Num_a значение Num_a, делен-ное на Num_b. Обратите внимание, что после успешного выполнения этогооператора значение Num_a изменится.

Третий оператор присваивает переменной Num_b значение 7.

Секция исключенийВ ходе выполнения PL/SQL-оператора может возникнуть ошибка, которая

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

PL/SQL помогает вам во всех этих случаях, предоставляя средства обработ-ки исключений (exception handling). В хорошо написанных приложениях исклю-чения столь важны, что им посвящен специальный раздел в конце этой главы,где о них рассказано более подробно. А здесь, в качестве введения, рассмотримструктуру секции исключений.

EXCEPTIONWHEN имя_исключенияTHEN

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

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

Секция исключений начинается с ключевого слова EXCEPTION и продол-жается до конца блока. Каждому исключению соответствует оператор WHENимя_исключения, указывающий, что должно быть сделано при возникновенииданного исключения. В нашем примере таких операторов три, все они выводяттекст на экран SQL*Plus. Чтобы понять их работу, требуется немного дополни-

Введение в PL/SQL 263

тельной информации, но мы отложим выяснение подробностей до главы 9. Па-кет DBMS_OUTPUT и процедура PUT_LINE являются частью базы данныхOracle; вместе они позволяют построчно отображать текст на экране SQL*Plus.

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

Выполнение оператора, указанного в секции исключений, называется обра-боткой исключения (exception handling).

Процесс, включающий в себя обнаружение ошибки, определение, какоеисключение описывает ее наилучшим образом, и передачу PL/SQL информа-ции, позволяющей найти соответствующий код в секции исключений, называ-ется возбуждением исключения (raising exception). В демонстрационном кодеисключение возбуждает сам PL/SQL, обнаружив попытку деления на нуль.В PL/SQL это исключение имеет предопределенное имя — ZERO_DIVIDE.Во многих ситуациях ошибку должен обнаруживать ваш код, а не PL/SQL.

Создание простой PL/SQL- процедурыСейчас у нас есть все компоненты, необходимые для написания закончен-

ной PL/SQL- процедуры. Вы узнали о базовом блоке и познакомились со спе-цификациями процедур. Введите следующий код:

CREATE PROCEDURE my_f irst_proc IS

greetings VARCHAR2 (20) ;BEGIN

greetings := 'Hello World';

dbms_output .put_line (greetings) ;

END my_f irst_proc;

/

Синтаксис создания хранимой процедуры имеет вид:

CREATE PROCEDURE спецификация_процедуры IS тело_процедуры

В нашем примере спецификацией процедуры является ее имя, а телом —все, что идет дальше, вплоть до завершающей точки с запятой. При созданиифункции ключевое слово PROCEDURE заменяется на FUNCTION:

CREATE FUNCTION спецификация _функции IS тело_функции

Прямой слэш (/) сообщает PL/SQL, что ввод программы завершен и нужноперейти к выполнению команд. Процедуру или функцию можно создать зано-во, использовав команду CREATE OR REPLACE вместо CREATE. Это приве-дет к уничтожению старого определения и замене его на новое. При отсутствиистарого определения будет просто создано новое.

CREATE OR REPLACE спецификация _процедуры IS тело_процедуры

Теперь посмотрим, как можно вызывать эту процедуру из SQL*Plus:. ; ' ' .. •

set serveroutput onEXECUTE my_first_proc;

264 Глава 8

Команда SET SERVEROUTPUT ON позволяет увидеть выходные данные.Команда EXECUTE запускает процедуру на выполнение. Вы также можете вы-звать процедуру из анонимного блока, как показано ниже. Сравните получен-ные результаты с показанными на рис. 8.2.

BEGIN

END;

my_first_proc;

Вызов процедур и функцийПроцедура или функция может иметь формальные параметры как со значе-

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

*. Oracle SQL'Plus

File Edit Search Options Help

SQL> set serueroutput onSQL> CREATE PROCEDURE my_first_proc

2 IS3 greetings UflRCHflR2(20);4 BEGIN5 greetings := 'Hello World

1;

6 dbms_output.put_line(greetings);7 END n»y_f irst_proc;8 /

Procedure created.

SQL> EXECUTE ny_first_proc;Hello World

PL/SQL procedure successfully completed.

SQL> BEGIN2 ny_first_proc;3 END;>t /

Hello World

PL/SQL procedure successfully completed.

SQL>

JU

mmmi

J

Рис. 8.2. Простая PL/SQL-процедура "Hello World"

Введение в PL/SQL 265

• Типы данных фактических параметров должны совпадать с типамиданных соответствующих формальных параметров или допускатьпреобразование в них.

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

При вызове процедуры без каких-либо параметров можно указывать толькоее имя, со скобками или без скобок:

имя_процедуры();

или

имя_процедуры;

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

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

.имя_процедуры(факт_парам_ 1,

факт_парам_2,

факт_парам_1*Г) ;

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

Если формальные параметры со значениями по умолчанию не являются по-следними в спецификации или желательно избежать сопоставления фактиче-ских параметров с формальными по их расположению в списке, можно явноустановить соответствие между фактическими и формальными параметрами,воспользовавшись следующим синтаксисом:

имя_процедуры(форм_парам_1 => факт_парам_1,форм_парам_2=> факт_парам_2,

Это называется именованной нотацией (named notation) вызова функций ипроцедур. Приведенная ранее нотация называется позиционной (positional), по-скольку сопоставление параметров основано на их позиции в списке.

К функциям применимы те же методы вызова. Однако функции могут появ-ляться внутри других выражений и, соответственно, не иметь в конце точки с

266 Глава 8

запятой. Примеры именованной нотации вы увидите в следующем разделе.Можно смешивать две нотации, но позиционный список должен предшество-вать нотационному в списке параметров.

Переменные и константы PL/SQLВ предыдущих разделах вы видели некоторые примеры переменных

PL/SQL. Теперь рассмотрим их подробнее. По существу, переменные — этоименованные контейнеры. Они могут содержать информацию (данные) раз-личных видов. В зависимости от того, какую информацию в них можно поме-щать, они имеют различные типы данных, а чтобы отличать их друг от друга, имприсваиваются имена. Подобно тому как масло разливают в бутылки, а мукузасыпают в бумажные пакеты, PL/SQL хранит числа в переменных типаNUMBER, а текст — в переменных типа CHAR или VARCHAR2. Продолжаяэту аналогию, представьте себе холодильник в комнате отдыха вашей компа-нии. Он наполнен коричневыми бумажными пакетами с порциями ланча длявас и ваших коллег. Как вы поступите, чтобы не потерять свой пакет среди дру-гих? Правильно, напишете на нем свое имя. Переменным тоже даются имена,чтобы избежать путаницы. Далее, если ваш ланч состоит из одних бананов, выможете съесть их и положить шкурки обратно в бумажный пакет. В результатесодержимое пакета изменится. Подобно этому, содержимое переменных мож-но менять в ходе выполнения PL/SQL-операторов.

Объявление переменных PL/SQLСинтаксис объявления переменной в PL/SQL может иметь любую из следу-

ющих форм:

имя_переменной тип_данных [[NOTNULL] := выражение_по_умолчанию\;

имя_переменной тип_данных [[NOT NULL] DEFAULTвыражение_по_умолчанию];

Имя_переменной — это любой правильный идентификатор PL/SQL. Пра-вильный идентификатор PL/SQL должен:

• Иметь не более 30 символов в длину и не содержать пробельныхсимволов (собственно пробелов и знаков табуляции).

• Состоять только из букв, цифр от 0 до 9, символа подчеркивания (_),знака доллара ($) и знака фунта (#).

• Начинаться с буквы.

• Не совпадать с зарезервированными словами PL/SQL или SQL, которыеимеют специальное значение. Например, именем переменной не можетбыть слово BEGIN, которое обозначает начало выполняемой секциибазового блока PL/SQL.

Тип_данных — это любой допустимый тип данных SQL или PL/SQL. Допол-нительная информация о типах данных будет приведена в следующем разделе.

Модификатор NOT NULL требует, чтобы переменная имелазначение. Еслион указан, переменной должно быть присвоено значение по умолчанию.

Введение в PL/SQL • ______^__ 267

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

Вы уже знаете о типах данных SQL — NUMBER, VARCHAR2 и DATE. Кро-ме них PL/SQL имеет дополнительные типы данных, отсутствующие в SQL.Полный их список можно найти в справочниках по PL/SQL, издаваемых кор-порацией Oracle. г . >;

Объявление констант PL/SQLСинтаксис объявления константы имеет следующий вид;

имя_переменной тип_данных CONSTANT := выражение;

В отличие от переменных константам обязательно присваивается значение,которое нельзя изменять на протяжении времени жизни константы. Констан-ты очень полезны для поддержания безопасности и дисциплины при разработ-ке больших и сложных приложений. Например, если вы хотите гарантировать,что процедура PL/SQL не будет модифицировать передаваемые ей данные, мо-жете объявить их константами. Если процедура все же попытается их модифи-цировать, PL/SQL возбудит исключение.

Присваивание значений переменным ,„Есть три способа изменения значения переменной. Во-первых, ей можно

присвоить значение выражения, использовав оператор присваивания PL/SQL.Вы уже видели ряд примеров такого сорта. Вот соответствующий синтаксис:

имя_переменной := выражение;

Во-вторых, переменная может быть передана PL/SQL-процедуре в качествефактического параметра, соответствующего одному из формальных парамет-ров IN OUT или OUT. После завершения процедуры значение переменной мо-жет измениться. Это демонстрируется в приведенном ниже примере, где длявызова процедуры использована именованная нотация. Ожидаемые результа-ты показаны на рис. 8.3.

CREATE PROCEDURE hike_priees (oldjprice NUMBER,

percent_hike NUMBER := 5,new_price OUT NUMBER)

ISBEGIN

new_price := old_price + old_price * percent_hike / 100;

END hike_prices;

/

DECLARE

price_to_hike NUMBER(6,2) := 20;

hiked_price NUMBER(6,2) := 0;

BEGINdbms_output.put_line('Price before hike ' I I price_to_hike);

dbms_output.put_line('hiked_price before hike ' I I ..

hiked_price);hike_prices (old_price => price_to_hike,

268 Глава8

END;

new_price => hiked_price) ;dbms_output.put_line ( 'price_to_hike after hike ' |

price_to_hike) ;

dhms_output .put_line ( 'hiked_price after hike ' [|

hiked_price) ;

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

DECLARE

product_quant NUMBER;

BEGIN

SELECT quantity_on_hand

INTO product_quant

FROM plsq!101_product

WHERE product_name = 'Small Widget';

dbms_output .put_line ('Small Widget ' I I product_quant) ;

END;

*OfacleSQlilPlu»-

file Ed» Jeaich Ш*оп» IMPSQL> set serueroutpyt onSQL> CREATE PROCEDURE hike_prices (old_price NUHBER,

IS

BEGIN

new_priceEND hike prices;

percentjiike NUMBER :-new_pcice OUT NUMBER)

5,

old_price * old_price • percent__hike / 100;

Procedure created.

SQL> DECLARE

23и5б7

891в1112

BEGIN

END;

price to hike NUH8ER(6,2) := 20;hikedjrice HUMBER(6,2) := U;

dbras_output.put_line("Price before hike • || price_to_hike);dbms~output.put_line('hiked_price before hike ' 1 1 hiked_price);hike_prices (old_price •> price_to_hike,

neu_price => hiked_price);dtins_output.put_line( 'price_to_hike after hikedbns_output.put_line{'hiked_price after hike '

' || price_to_hike);Ц hiked_price);

Price before hike 28hiked_price before hike Dprice_to hike after hike 20hiked_price after hike 21

PL/SQL procedure successfully completed.

SQL>

Рис. 8.3. Присваивание значений переменным PL/SQL путемподстановки в качестве фактических параметров

Введение в PL/SQL 269

• * Oiacle SQL'Plus

Efe & Se«ch Qptions НфSQL> set serveroutput onSQL> DECLARE

2 product quant NUMBER;3 BEGIN4 SELECT quantity_on_hand5 INTO product quant6 FROM plsqUOl product7 WHERE product name - 'Small Widget';89 dons output. put line ('Snail Widget

10 END;11 /

Small Widget 1

PL/SQL procedure successfully completed.

SQL>

НЗЕЭ•••«•••••••••••РИРИИвИвИвИ ИНвРИ

г!

!| product_quant);

A

I

Рис. 8.4. Присваивание значений переменным PL/SQLс использованием SQL

Параметру product_quant присваивается значение, равное количеству SmallWidget.

Использование переменныхПеременные являются фундаментальными компонентами PL/SQL-npo-

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

Предположим, что вам нужно выполнить вычисления с использованием те-кущего количества Small Widget — сравнить его с тем, которое было три месяцаназад, или с количеством Medium Wodget. Использовав для хранения этого ко-личества переменную, вы избежите задержек, связанных с многократным счи-тыванием значения из таблицы.

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

Управляющие структуры в PL/SQLВо многих случаях программа должна выполнять разные действия в зависи-

мости от того, выполнено ли некоторое условие. Например, если сумма заказапревышает одну величину, делается скидка в 5%, а если сумма превышает дру-гую величину, скидка увеличиваетсядо 10%. Такая же логика может потребова-ться в приложении, которое распечатывает окончательные счета для клиентов.Это называется условной обработкой (conditional processing) данных. В зависи-мости от результата проверки условия выполняются разные части кода.

270 Глава 8

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

PL/SQL дает возможность выполнять условную и итеративную обработку.Предоставляемые им конструкции изменяют ход выполнения программы(program flow), управляя последовательностью выполнения (flow of execution).

Оператор IFОператор IF имеет следующий синтаксис:

IF условие_1 THENдействие_1;

[ELSIF условие_2ТНЕ^действие_2',]

[ELSEальтернативное_действие\]

END IF;

Действие_1... альтернативное Действие представляют один или несколькоPL/SQL-операторов. Каждая группа операторов выполняется только в том слу-чае , если выполнено соответствующее условие. После того как обнаружено вы-полнение одного из условий, остальные условия не проверяются.

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

— Расчет скидки на заказ.— Входной параметр - сумма заказа. Возвращается сумма скидки— (нуль при неверных входных данных).CREATE FUNCTION compute_discounts (order_amt NUMBER)RETURN NUMBER IS

small_order_amt NUMBER := 400;large_order_amt NUMBER := 1000;small_disct NUMBER := 1;large_disct NUMBER := 5;

BEGINIF (order_amt < large_order_arat

ANDorder_amt >= small_order_amt)

THENRETURN (order_amt * small_disct / 100) ;

ELSIF (order_amt >= large_order_amt)THEN

RETURN (order_amt * large_disct / 100);ELSE

N

RETURN(0);END IF;

END compute_discounts;

Введение в PL/SQL 271

Ж Oiacle SQL-Plus

File Edit Seated Options Help

SQL> CREATE FUNCTION compute_discounts (order_ant NUMBER)2 RETURN NUMBER

3 IS

small_order_amt NUMBER := 400;

large_order_amt NUMBER :- 1000;

sroall_disct NUMBER := 1;

large_disct NUMBER :- 5;

|x

-l]

45678 BEGIN

9H1112131*1516171819

IF (order_amt < large_order_amt

AND

order_amt >= small_order_amt)

THEN

RETURN (order_amt « small_disct / 1BO);

ELSIF (order_amt >- large_order_amt)

THEN

RETURN (order_amt « large_disct / 180);

ELSE

RETURN(O);

END IF;20 END compute_discounts;

21 /

Function created.

BEGIN

SQL> DECLARE

2345б789

101112

.13END;

/

tiny NUMBER := 20;

med NUMBER := 600;

big NUMBER := 4550;

wrong NUMBER := -35;

dbms_output.put_line (' Order

dbms_output.put_line (tiny || •

dbms_output.put_line (med |j '

dbms_output.put_line (big j| '

dbns output.put_line (wrong ||

AND Discount ');

'|| compute discounts(tiny)) ;

|| compute_discounts (med));

I I compute discounts (big));

' || conpute_discounts (wrong));

Order

20 0

«ao 64550

-35

AND Discount

227.5

PL/SQL procedure successfully completed.

SQL>

-

Рис. 8.5. Пример оператора IF

Эта функция будет делать скидку в I % для заказов на сумму от 400 до 1000, искидку в 5% для заказов на сумму более 1000. Для всех остальных значений(включая неправильные) она будет возвращать нуль. К неправильным значе-ниям относятся, например, отрицательные числа.

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

10 Зак. 725

272 Глава 8

Вы можете протестировать функцию, вызвав ее из анонимного блока. Не за-будьте про команду set serveroutput on. Как и в предыдущем примере,:ориенти-руйтесь на рис. 8.5.

I DECLARE

tiny NUMBER := 20;

med NUMBER := 600;

big NUMBER := 4550;

wrong NUMBER := -35;

BEGIN

dbms_output.put_line (' Order AND Discount ');dbms_output.put_line (tiny || ' ' I f compute_discounts(tiny));

dbms_output.put_line (med || ' ' I I compute_discounts (med));

dbms output.put_line (big I I ' ' || compute_discounts (big));

dbms_output.put_line (wrong || ' ' I I

compute_discounts (wrong));

END;

/

ЦиклыPL/SQL предоставляет три различные конструкции для итеративной об-

работки. Каждая из них позволяет циклически выполнять набор операто-ров PL/SQL. Выход из цикла осуществляется в зависимости от некоторогоусловия.

LOOPКонструкция LOOP имеет следующий синтаксис:

« имя_цикла»LOOP

операторы;EXIT имя_цикла [WHEN условие_выхода}\операторы',

END LOOP;

При наличии конструкции WHEN все операторы в теле цикла повторяютсядо тех пор, пока выражение условие_выхода не примет положительное значе-ние (т.е. не станет истинным). Условие выхода проверяется на каждом проходе,иначе называемом итерацией. Как только выражение принимает значение "ис-тина", все операторы после EXIT пропускаются, итерации прекращаются и вы-полнение продолжается с первого оператора, следующего за END LOOP. Еслиусловие WHEN отсутствует, операторы между LOOP и EXIT выполняются то-лько один раз. Очевидно, что опустив условие WHEN, вы поступите нелогич-но. В конце концов идея цикла состоит в том, чтобы обеспечить потенциальномногократное выполнение кода.

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

Как всегда, не забудьте ввести команду set serveroutput, чтобы увидеть вы-ходные данные.

Введение в PL/SQL 273

File £dit Search Qptions Help

SQL> set serueroutput onSQL> DECLflRE

2 just_a_nun NUMBER := 1;3 BEGIN

*567891011 END;12 /123ll5678910

4 I«just a loop»LOOP

dbns_output .put_line( just_a_nun) ;EXIT just a loopWHEN (just_a_num >= 10);

just a nun := just a nun + 1;END LOOP; "

• ' ;

• '•" ; '-' :' • ' ' • - . •:'••''..

. , •' ,

' ' ' •' - ' ' .

,

Л

• • .

•' ' :

'• '•••".-.•

PL/SQL procedure successfully completed.

SQL>

JJ

л. . . . . . , ' • . . .... : ^Гл

j

: ' - ""

Рис. 8.6. Пример простого цикла

DECLARE

BEGIN

just_a_num NUMBER := 1;;

«just_a_loop»LOOP

dbms^output ,put_line (just_a_num) ;

EXIT just_a_loop

WHEN (just_a_num >= 10);just_a_num := just_a_num + 1;

END LOOP;

END;

Каждая итерация увеличивает переменнуюуи^_й_им/и на 1. По достижениизначения 10 выполняется условие выхода и цикл завершается.

ю*

274 Глава 8

Цикл WHILEЕще одной разновидностью цикла является цикл WHILE. Он хорошо под-

ходит в ситуациях, когда количество итераций заранее неизвестно, и определя-ется некоторым внешним фактором. Цикл WHILE имеет следующийсинтаксис:

WHILE условие_выходаLOOP

операторы;END LOOP;

Чтобы потренироваться в создании цикла WHILE, введите следующий код исравните результаты с показанными на рис. 8.7:

.

* Oracle SQL'Plus

File Edit Search Options Help

SQL> set serveroutput onSQL> DECLARE

just_a_num NUMBER := 1;

WHILE (just_a_num <= 18)LOOP

dbns_output.put_line(just_a_num);just_a_num := just_a_num + 1;

END LOOP;

3 BEGINi»56789 END;10 /

123

Ц56

78910

PL/SQL procedure successfully completed.

SQL>

Рис. 8.7. Пример цикла WHILE

Введение в PL/SQL 275

-DECLARE

just_a_num NUMBER := 1;

BEGIN

WHILE (just_a_num <= 10) LOOPdbms_output .put_li'ne (just_a_num) ;

just_a_num := just_a_num + 1;

END LOOP;

END;

/

Условие WHILE проверяется перед каждым входом в цикл. Если оно имеетзначение "истина", то выполняется очередная итерация.

Цикл FORВ цикле FOR для подсчета итераций используется переменная-счетчик, на-

зываемая также индексом цикла (loop index). По завершении каждой итерациисчетчик увеличивается, начиная с нижнего предела, или уменьшается, начинаяс верхнего предела. Как только его значение выйдет за указанный диапазон,цикл завершается. Синтаксис цикла FOR выглядит следующим цбразом:

FOR счетчик IN [REVERSE] нижняя^граница .. верхняя граница :,LOOP

операторы;END LOOP;

Теперь создайте свой первый цикл FOR, использовав приведенный ниже,код. Сверяйтесь с рис. 8.8.

BEGIN

FOR just_a_num IN 1..10

LOOPdbms_output.put_line(just_a_num);

END LOOP;END;/

Теперь, ради интереса (а также для приобретения опыта), попробуйте ис-пользовать в этом цикле FOR команду REVERSE. В результате числа должныбыть показаны в обратном порядке — от 10 до 1.

КурсорыКурсор — это исключительно важная конструкция PL/SQL, лежащая в

основе взаимодействия PL/SQL и SQL. Название "курсор" означает "текущийнабор записей". Курсор представляет собой специальный элемент PL/SQL, скоторым связан SQL-оператор SELECT. Используя курсор, можно отдельнообрабатывать каждую строку связанного с ним SQL-оператора. Курсор объяв-ляется в секции объявлений базового блока. Он открывается командой OPEN,а выборка строк осуществляется с помощью команды FETCH.

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

276 Глава 8

.+. Oracle SQL'PlL

File

SQL>SQL>

231567

§1 '23kS678918

£dil Search Qptions Helpset serueroutput onBEGIN

END;/

FOR just_a_nun IN 1..10LOOP

dbms_output.put line(just_a_num);END LOOP;

PL/SQL procedure successfully completed.

SQL>

-iJJ

Рис. 8.8. Пример цикла FOR

Закрытие курсора или выполнение явной операции COMMIT или ROLLBACKприведет к разблокированию строк.

Для SQL-операторов, используемых в коде PL/SQL, применяются скрытые,или неявные (implicit), курсоры. Мы рассмотрим их в следующей главе, а в этом раз-деле сосредоточимся на явных (explicit) курсорах, т.е. тех, которым присвоено имя.

Далее мы напишем простую процедуру, которая использует курсор для вы-числения комиссионных каждого продавца. Однако перед тем, как этим заня-ться, рассмотрим синтаксис явного курсора.

Объявление курсора и атрибуты курсораКурсор объявляется в процедуре PL/SQL следующим образом:

CURSOR имя_курсора [([параметр_1 [, параметр_2...])][RETURN спецификация возврата]IS

оператор_select[FOR UPDATE

[OF таблица_или_столбец_1[, таблица_или_столбец_2...]

Введение в PL/SQL 277

Параметры курсора похожи на параметры процедуры, за тем исключением,что они веегда являются входными (IN). Использование параметров OUT илиIN OUT невозможно, поскольку курсор не может их модифицировать. Пара .метры используются в конструкции WHERE курсорного оператора SELECT.Спецификация возврата показывает, записи какого типа будут выбиратьсяоператором SELECT. Подробнее о записях PL/SQL говорится в следующейглаве. Таблица_ши_столбец — это имя столбца, который предстоит обновлять,или имя таблицы, в которой предстоит удалять или обновлять строки. Оно дол-жно входить в число имен таблиц и столбцов, указанных в операторе SELECTкурсора, и предназначено для документирования, показывая, какие элементымогут быть потенциально модифицированы кодом, использующим данныйкурсор. Команда FOR UPDATE блокирует строки, выбранные операторомSELECT при открытии курсора. Строки остаются заблокированными до техпор, пока вы не закроете курсор рассмотренными выше способами.

Курсор имеет ряд индикаторов, показывающих его состояние. Они называ-ются атрибутами курсора и приведены в таблице 8.1.

Таблица 8.1. Атрибуты курсора

Атрибут Описание

имя_курсора%180РЕМ Позволяет проверить, открыт ли курсор. Если курсоримя_курсора уже открыт, возвращается значение TRUE

имя_курсора%РЮ\Л/СОимт Количество строк таблицы, возвращенных операторомSELECT курсора

имя_курсора%РОиш Позволяет проверить, была ли успешной последняя попыт-ка получения записи из курсора. Если запись была выбрана,возвращается значение TRUE

имя_курсора%МОТГОимо Противоположен атрибуту FOUND. Если записей больше ненайдено, возвращается значение TRUE

Записи PL/SQLЗаписи PL/SQL детально рассматриваются в следующей главе, однако вам

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

Запись PL/SQL— это набор данных базовых типов. К ней можно обращать-ся, как к единому целому. Для доступа к отдельным полям записи применяетсянотация имя_записи.имя_поля, которую вы уже использовали для столбцов таб-лицы. Записи могут иметь один из трех типов, перечисленных ниже; вы можетеобъявлять переменные, имеющие тип записи.

• Основанные на таблице (table-based) Эти записи имеют поля,совпадающие по имени и типу со столбцами таблицы. Если курсорвыбирает всю строку — например, оператором SELECT * FROMнекоторая_таблица — то возвращаемые им записи можнонепосредственно копировать в переменную, имеющую тип записи,основанной на таблице некоторая_таблица.

278 ГлаваВ

• Основанные на курсоре (cursor-based) Поля этих записей совпадают поимени, типу и порядку с заключительным списком столбцов вкурсорном операторе SELECT.

• Определенные программистом (programmer-defined) Это записи, типкоторых определяете вы сами.

Использование команд OPEN, FETCH и CLOSEКоманды открытия курсора, выборки из курсора и закрытия курсора имеют

следующий синтаксис:

OPEN имя курсора;. — : " * , - » . . . . ( . . . . „ . . . . . . , , . . .

FETCH имя_курсора INTO переменная_или_список_пвременных',

CLOSE имя_курсора;

После открытия курсор содержит набор записей, если в результате успешноговыполнения оператора SELECT из базы данных были выбраны заданные стро-ки. Каждая команда FETCH удаляет запись из открытого курсора и перемещаетее содержимое либо в переменную PL/SQL, тип записи которой совпадает с ти-пом записи курсора, либо в группу переменных PL/SQL, где каждая переменнаяв списке совпадает по типу с соответствующим полем в записи курсора.

Перед тем как пытаться выбрать из курсора очередную запись, следует про-верить с помощью атрибутов FOUND и NOTFOUND, есть ли в нем еще запи-си. Выборки из пустого курсора будут все время давать последнюю запись, неприводя к ошибке. Не забывайте проверять атрибуты FOUND и NOTFOUNDпри использовании FETCH.

Фактическая обработка записей из курсора обычно выполняется внутрицикла. При написании такого цикла неплохо начать с проверки, была ли най-дена запись в курсоре. Если да, можно продолжать необходимую обработку; впротивном случае следует выйти из цикла. То же самое можно сделать более ко-ротким путем, использовав курсорный цикл FOR. При этом PL/SQL будет осу-ществлять открытие, выборку и закрытие без вашего участия.

Курсорный цикл FORСинтаксис курсорного цикла FOR имеет следующий вид:

: • ' : •- '

FOR запись_курсора IN имя_курсора LOOPоператоры',

END LOOP;

Этот цикл выбирает записи из курсора в переменную типа запись_курсора.Поля записи_курсора можно использовать для доступа к данным из операторовPL/SQL, выполняемых в цикле. Когда все записи выбраны, цикл завершается.Для удобства открытие и закрытие курсора производится автоматически.

Попытавшись выбрать запись из неоткрытого курсора, вы получите сооб-щение in valid cursor (недействительный курсор). Если не закрывать курсоры, тов конце концов количество открытых курсоров достигнет максимальной вели-чины, допускаемой системой. Имейте в виду, что неявные курсоры, которыебудут рассмотрены позже, тоже вносят вклад в достижение этого предела.

Введение в PL/SQL 279

Конструкция WHERE CURRENT OFКогда курсор открывается для обновления или удаления выбранных запи-

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

WHERE CURRENT OF имя_курсора

для доступа к таблице и строке, которые соответствуют последней записи, вы-бранной в конструкции WHERE оператора UPDATE или DELETE. Это демон-стрируется в приведенном ниже коде, который снижает цены в таблицеPLSQL101_PRODUCT на 3%. Введите его и сравните результаты с показанны-ми на рис. 8.9.

1 * Oracle SQL-Plus . - • * * < • •

F»e £dit Seach flpUons Help

Small Widget 99Medium Uodget 75Chrome Phoobar SORound Chrome Snaphoo ZSExtra Huge Mega Phoobar * 8.96Square Zinculator 40.5flnodized Franifier W.1Red Snaphoo 1.76Blue Snaphoo 1 .76

1 /9 rows selected.

SQL> DECLflRE2 CURSOR product cur IS3 SELECT » FROM plsqUOl product>t FOR UPDflTE OF product price;5 BEGIN6 FOR product rec IN product cur7 LOOP8 UPDflTE plsqll Byproduct9 SET product price - (product rec. product price10 , WHERE CURRENT OF product cur;11 END LOOP;12 END;13 / i :, • ' • .

PL/SQL procedure successfully completed.

SQL> SELECT product name, product price2 FROM plsq!1B1 product;

PROOUCT_NflNE PRODUCT_PRICE

Snail Midget 96.03Medium Uodget 72.75Chrome Phoobar 48.5Round Chrome Snaphoo 24.25Extra Huge Mega Phoobar * 8.69Square Zinculator 39.29Anodized Framif ier , 42.78Red Snaphoo 1.71Blue Snaphoo 1 .71

1 - '- •- • ' •, *•' • • .• • '.9 rows selected.

№ <

НИИ!

,

« 0.97)

Л

:

Рис. 8.9. Пример курсорного цикла FOR и конструкции WHERE CURRENT OF

280 Глава 8

SELECT product_hame, product_price

FROM plsq!101_product;

DECLARECURSOR product_cur IS

SELECT * FROM plsql!01_product

FOR UPDATE OF product_price;

BEGINFOR product_rec IN product_cur

LOOP

UPDATE plsql!01_productSET product_price = (product_rec.product_price * 0.97)

WHERE CURRENT OF product_cur;

END LOOP;

END;

/• - ' • ,

SELECT product_name, product_price

FROM plsqllOljproduct;

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

также циклов внутри циклов, или вложенных циклов (nested loops).

— Эта процедура подсчитывает комиссионные продавцов.

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

— и соответствующие комиссионные.

-- Входные данные: отсутствуют. Об ошибках не сообщается, исключения

-- не возбуждаются./* Логика: создается курсор для соединения PLSQL101_PRODUCT и

PLSQL101_PURCHASE по столбцу PRODUCT_NAME. Результат упорядочивается

по продавцам. Внешний цикл перебирает всех продавцов, а внутренний

цикл обрабатывает все строки для одного продавца.

*/CREATE OR REPLACE PROCEDURE do_commissions IS

commission_rate NUMBER := 2

total_sale NUMBER := 0current_person CHAR(3) := ' '

next_person CHAR(3)

quantity_sold NUMBER := 0

item_price NUMBER := 0CURSOR sales_cur IS

-SELECT tabl.salesperson, ..... . ,

tabl.quantity,

tab2.product_price

FROM plsqll01_purchase tabl,

plsql!01_product tab2WHERE tabl.product_name ="tab2.product_name

;

ORDER BY salesperson;BEGIN

OPEN sales_cur;

LOOP

FETCH sales cur INTO

Введение в PL/SQL 281

next_person, quantity_sold, item_price;

WHILE (next_person = current_person

AND

sales_cur%FOUND)

LOOP

total_sale :=

total_sale + (quantity_sold * item_price);

FETCH sales_cur INTO

next_person, quantity_sold, item_price;

END LOOP;IF (sales_cur%FOUND)

THENIF (current_person != next_person)

THEN

IF (current_person != ' ' )

THEN

dbms_output.put_line

(current_person ||1 ' I Itotal_sale ||

. . . . ' ' I Itotal sale * cbmmission_rate'

/ 100);

END IF;total_sale := quantity_sold * item_price;

current_person := next_person;

END IF;ELSE IF (current_person != ' ')

THEN

dbms_output.put_line(current_person ||1 ' I Itotal_sale I I1 ' I Itotal_sale * commission_rate

/ 100);

END IF;

END IF;EXIT WHEN sales_cur%NOTFOUND;

END LOOP;

CLOSE sales_cur;

END do_commissions;/

В первую очередь, посмотрите на курсорный оператор SELECT. Он извле-кает количество проданных товаров из таблицы PLSQL101_PURCHASE, а ихцены — из таблицы PLSQL101_PRODUCT. Это делается при помощи соедине-ния. Результат упорядочивается по продавцам, чтобы записи, относящиеся котдельному продавцу, располагались вместе.

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

282 Глава 8

первому оператору IF. Он проверяет, возвращала ли последняя команда FETCHкакую-нибудь запись. Если запись была возвращена, значения current person иnext_person проверяются на совпадение. Если они не совпадают, последняя вы-борка относится к новому продавцу, т.е. пора распечатать комиссионные длятекущего продавца. Обратите внимание, что первое значение current_person невходит в число допустимых, поэтому условие в третьем IF не выполняется и пе-чать не производится.

Следующий оператор заносит в переменную totaljsale цену самого первоготовара. Затем значение next_person сохраняется в переменной current_person.После этого мы возвращаемся к оператору FETCH в начале цикла, посколькуусловие выхода из цикла еще не выполнено. В результате выборки значениеnext_person может оказаться таким же, как и current_person, а это означает, чтотекущему продавцу соответствует более одной записи в списке продаж. В этомслучае происходит вход в цикл WHILE и цена товара, умноженная на продан-ное количество, добавляется к общему объему продаж, выполненных даннымпродавцом. Выборка новых записей из курсора и суммирование цен продолжа-ются до тех пор, пока не будет идентифицирован новый продавец. Весь процессповторяется снова и снова, пока не закончатся записи в курсоре. В этой точкезначение current_person проверяется на допустимость. При положительном ре-зультате проверки самый последний оператор IF распечатывает объем продажи комиссионные для этого продавца. Для расчета комиссионных используетсяконстанта commission_rate.

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

SELECT tabl.salesperson,

tabl.quantity,

tab2.product_price

FROM plsql!01_purchase tabl,plsql!01_product tab2

WHERE tabl.product_name = tab2.product_nameORDER BY salesperson;

EXECUTE do_commissions;

Обработка ошибокПри возникновении исключительных ситуаций важно выдавать дружест-

венные к пользователю сообщения об ошибках. Об исключениях уже упомина-лось в разделе этой главы, посвященном базовым блокам PL/SQL. Теперьнастало время рассмотреть их подробнее.

1 • • • ' ' ' " : • • ' - .

ИсключенияИсключение — это состояние ошибки, которое активизируется — wot воз-

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

Введение в PL/SQL 283

т Oracle SQL'Plus

File £dit Search Options Help

61 END IF;62 END IF;636465 END LOOP;66 CLOSE sales_cur;67 END do_commissions;68 /

Procedure created.

НООЕЗ

EXIT WHEN sales cur%NOTFOUND;

SQL>2345678

SELECT

FROM

WHEREORDER BV

tabl.salesperson,tabl. quantity,tab2.product_priceplsq!181_purchase tabl,plsq!181_product tab2tab1.product_nane = tab2.product_naroesalesperson;

SflL QUflNTITV PRODUCT_PRICE

BBСиСиСиGAGALB

751518

228

72.7596.8324.2596.8396.03

48.572.75

7 rows selected.

SQL> EXECUTE do_commissions;BB 5456.25 189.125CA 313.31 6.2662

GA 865.24 17.3848LB 1455 29.1

PL/SQL procedure successfully completed.

SQL>

A-l ^^__^___ AРис. 8.10. Пример вложенных циклов

При возникновении исключительной ситуации выполнение кода останавли-вается на операторе, который возбудил исключение, и управление передаетсятой части блока, которая обрабатывает это исключение. Если блок не содержитвыполняемой секции, PL/SQL пытается найти выполняемую секцию во вклю-чающем базовом блоке (enclosing basic block), т.е. в блоке, который являетсявнешним по отношению к коду, возбудившему исключение. Если в непосред-ственном включающем блоке отсутствует обработчикданного исключения, топоиск продолжается в блоках следующих уровней, пока не будет найден подхо-

284 Глава 8

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

Часть блока, предназначенная для обработки исключений, — это идеальноеместо для выдачи информативных сообщений об ошибках и выполнения очистки(cleanup), позволяющей избавиться от всего, что могло бы в дальнейшем вы-звать путаницу или проблемы. Если исключение было возбуждено в ходе вы-полнения процедуры, вставляющей строки в таблицу, то типичная процедураочистки может включать в себя оператор ROLLBACK.

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

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

PL/SQL. Существует довольно много других системных исключений, которыераспознаются и возбуждаются PL/SQL или Oracle. В таблице 8.2 приведен бо-лее полный список системных исключений.

В PL/SQL можно выдавать пользователям информацию об ошибке двумяспособами. Первый способ — использовать команду SQLCODE, которая воз-вращает код ошибки. Этот код представляет собой отрицательное число, обыч-но равное номеру ошибки ORA, которая выводится при завершенииприложения, если исключение осталось необработанным. Второй способ —возвращать текстовое сообщение, описывающее ошибку. Неудивительно, чтосоответствующая команда называется SQLERRM. В обработчике исключенияможно использовать как SQLCODE, так и SQLERRM. Замечание: не у всех си-стемных исключений есть имена.

Теперь вернемся к самому первому примеру этой главы и используем в немSQLCODE и SQLERRM. Введите следующий код и сравните результаты с по-казанными на рис. 8.11.DECLARE

Num_a NUMBER := 6;Num_b NUMBER;

BEGINNum_b := 0;Num_a := Num_a / Num_b;Num b := 7;

dbms_output.put_line(' Value of Num_b ' I I Num_b);EXCEPTION

WHEN ZERO_DIVIDE THENDECLARE

err_num .NUMBER := SQLCODE;err_msg VARCHAR2(512) := SQLERRM;

BEGtN

dbms_output.put_line('ORA Error Number ' || err_nuia ) ;dbms_.output.put_line('ORA Error message ' || err_msg) ;dbms_output.put_line(' Value of Num_a ' || Num_a);dbms_output.put_line(' Value of Num_b ' || Num_b);END;

END;

Введение в PL/SQL 285

Таблица 8.2. Системные исключения

Системное исключение

CURSOR.ALREADYOPEN

DUPVAL.ONJNDEX

INVAUD.CURSOR* ' • • ' . ' • '• '• .'• : • •

NO DATA FOUND

PROGRAM_ERROR

STORAGE.ERROR

TIME_OUT_ON_RESOURCE

TOO_MANY_ROWS

VAUUE_ERROR

ZERO.DMDE

OTHERS

Причина возбуждения

Попытка открыть уже открытый курсор

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

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

Попытка выполнить SELECT INTO, когда SELECT воз-вращает нулевое количество строк (а также другиепричины, описание которых выходит за рамки этойкниги)

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

Программе не хватает системной памяти

Программа слишком долго ожидала доступности неко-торого ресурса

SELECT INTO в PL/SQL вернул более одной строки

PL/SQL встретил неправильное преобразование илиусечение данных, или неправильное ограничение наданные

Попытка деления на нуль

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

286 Глава 8

Я Oracle SQL-Plus

£ie Edit Search Options Help

SQL> set serueroutput onSQL> DECLARE d

23456789

11111213111516171819

, 2 82122

nun a NUMBER :=' 6;num b NUMBER;

BEGINnum_d :- 0;nun a :* nun a / num b;пив_Ь :» 7;dbns output. put line(' Ualue of num b

EXCEPTIONWHEN ZERO DIUIDE

THEN

DECLARE

|| nun b);

BEGIN

END;

err nun NUMBER ;= SQLCODE;err msg UflRCHHR2(512) := SQLERRM;

dbns output. put_line('ORn Error Number 'dbms_output.put_line( 'ORft Error messagedbms output .put line( ' Ualue of num__a 'dbms output. put line(' Ualue of nun b '

| | err_nun ) ;' I I err_«sg);|| num_a);1 1 num_b) ;

END;/

ORA Error Number -1476ОНИ Error message ORA-Q1b76: diuisor is equal to zeroUalue of nun_a 6Ualue of num_b 0

PL/SQL procedure successfully completed.

SQL>

Рис. 8.11. Использование SQLCODE и SQLERRM при обработке системныхисключений

Исключения, определяемые программистомОдной из удобных возможностей PL/SQL является то, что он позволяет вам

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

Ниже приведен полный пример объявления и определения исключения.Введите этот код и сравните результаты с показанными на рис. 8.12.

set serveroutput onDECLARE

quantity!'NUMBER := -2;

quantity2 NUMBER := 3;

total NUMBER := 0;

quantity_must_positive EXCEPTION;

FUNCTION find_cost (quant NUMBER) .RETURN NUMBER ISi BEGIN

IF (quant > 0)THEN

RETURN (quant * 2 0 ) ;"ELSE '

END IF;RAISE quantity_must_pqsitive;

Введение в PL/SQL 287

END find_cost;

BEGIN

total := find_cost(quantity2);

total := total + find_cost(quantityl);

EXCEPTION

WHEN quantity_must_positiveTHEN

dbms_output.put_line('Total until now: ' || total);

dbms_output.put_line('Tried to use negative quantity ');

END;/

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

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

* Oracle SQL-PlusFife Edil Search Options Help

SQL> set serueroutput onSQL> DECLARE

234567891011121314151617181920212223

- -2;- 3;

quantityl NUMBER

quantity NUMBER

total NUMBER := В

quantity_must_positiue EXCEPTION;

FUNCTION find_cost (quant NUMBER) RETURN NUMBER IS

BEGINIF (quant > 0)

THEN

RETURN(quant * 20);

ELSE

RAISE quantity nust_positiue;

END IF;END find_cost;

BEGIN

total := Find_cost (quantity2);total := total + find_cost(quantitj|1);

EXCEPTION

WHEN quantity_must_positiue

THEN

dbns_output.put_line('Total until now: ' || total);dbms_output.put_line('Tried to use negatiue quantity ');

END;24Total until now: 6B

Tried to use negative quantity

PL/SQL procedure successfully completed.

SQL>

Рис. 8.12. Исключение, определенное программистом

288 Глава 8

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

ИтогиЭта глава послужила введением в удивительный мир PL/SQL — мощного

языка программирования, который работает в тесном сотрудничестве с SQL.Мы изучили переменные PL/SQL. Переменные используются для хранениярезультатов вычислений и передачи этих результатов от одного вычислитель-ного процесса к другому.

Мы всесторонне рассмотрели базовый блок PL/SQL. Каждая программнаяединица PL/SQL состоит из одного и более таких блоков. Базовый блок, в своюочередь, состоит из секции заголовка, секции объявлений, выполняемой сек-ции и секции исключений. Секция заголовка содержит идентификационныеданные блока. В анонимных блоках она пуста. Секция объявлений содержитобъявления переменных, констант, исключений, курсоров, функций и проце-дур, которые будут использоваться в выполняемой секции и секции исключе-ний; если ни один из этих элементов не используется, секция объявлений будетпуста. Выполняемая секция содержит выполняемые операторы PL/SQL. Этоединственная обязательная секция, ее присутствие необходимо для формиро-вания блока. Секция исключений используется для обработки исключитель-ных ситуаций, или исключений, возникающих в выполняемой секции. В ихчисло входят и те исключения, которые могут не обрабатываться во вложенныхблоках или в вызываемых функциях/процедурах.

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

Конструкции для управления выполнением программы позволяют одно-или многократно выполнять некоторую часть кода в зависимости от условия.Оператор IF обеспечивает однократное условное выполнение. Операторы цик-ла LOOP, WHILE и FOR обеспечивают повторное выполнение одного и тогоже набора операторов. Взаимодействие с SQL, а следовательно, с базой данных,осуществляется посредством курсоров. Курсорный цикл FOR позволяет обра-батывать строки таблиц по одной.

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

В этой главе мы рассмотрели много базовых понятий, заложив тем самымоснову для изучения главы 9. Вероятно, вам не терпится поэкспериментиро-вать с мощными средствами PL/SQL. Уделите этому некоторое время, а потомпереходите к следующей главе.

Введение в PL/SQL 289

Вопросы1. Что из сказанного ниже относительно функций и процедур PL/SQL

справедливо?

A. Между ними нет разницы.

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

C. И те, и другие могут иметь формальные параметры OUT или INOUT, но в функции такие параметры использовать не следует.

D. И те, и другие могут использоваться в конструкции WHERESQL-оператора SELECT.

ч . ' . ' . ' • . ' - • , . - . . - . ь . ' - ;

2. Что будет результатом выполнения следующего кода?

«outerjblock»DECLARE

scope_num NUMBER' := 3;... • BEGIN

DECLARE

scope_num NUMBER : = 6;Num_a NUMBER := outer_block.scope_num;

BEGINdbms_output.put_line(seope_num);dbms_output.put_line(Num_a);

END;dbms_output.put_line(scope_nura);

END; •

A. 6 3 3

B. Выполнение будет прервано с выдачей сообщения о повторном объ-явлении

C. 333

D. 6 3 6

3. Что из сказанного ниже относительно операторов IF справедливо?

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

' / : • : ' . ' - ' * • • ' ••/ . ; ' • - ' • • - ^ - • ; • - • . ' •. ' V': ' -, '

B. Возможны варианты. Иногда выполняется более одного набораоператоров, поскольку истинными могут быть одновременно asнесколько условий.

290 Глава 8

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

A. LOOP

B. WHILE. • • - ' . - . • . Г / v •• • '-: • . . . • • • • • . - : ,-• '

C. FOR

D. Курсорный FOR

5. Что из сказанного ниже относительно исключений неверно?

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

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

C. Когда система возбуждает исключения и они не обрабатываютсяпрограммистом, система не производит автоматический откат всехзавершенных изменений объектов базы данных (например,таблиц), сделанных в той выполняемой секции, где возниклоисключение.

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

Ответы на вопросы1.В, С.

Объяснение Разумеется, утверждение А неверно. Утверждение Dтакже неверно, поскольку процедуры в отличие от функций невозвращают значений, которые могли бы использоваться вконструкции WHERE. Функции вычисляют и возвращаютединственное значение, но не модифицируют входные данные,поэтому утверждение С верно. Справедливость утверждения В следуетиз синтаксиса PL/SQL.

2. А. 633

Объяснение Это пример областей видимости. Переменнаяscope_num внешнего блока перекрывается во внутреннем блоке егособственной переменной scope_num. Таким образом, во внутреннемблоке значением scope_num будет 6. Внутренняя переменная scope_numне видима во внешнем блоке, поэтому по завершении внутреннегоблока используется внешняя переменная scope_num и последнимвыводится значение 3. Чтобы получить значение внешней переменнойscope_num во внутреннем блоке, мы использовали метку внешнегоблока при присваивании значения переменной Num_a.

Введение в PL/SQL 291

3. А. Выполняется не более одного набора операторов

Объяснение При использовании IF, ELSE и ELSEIF выполнениеорганизуется таким образом, что условия являютсявзаимоисключающими. Только одно из них может быть истинным.Когда все условия ложны, не выполняются никакие операторы.

4. A. LOOP

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

5. В.

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

Глава

Другие средства PL/SQL

.

294 Глава9

Предыдущая глава содержала много базовых сведений о PL/SQL. Сейчас выможете писать законченные PL/SQL-процедуры и функции, а также использо-вать курсоры для взаимодействия с базой данных. В этой главе мы сосредото-чимся на неявных курсорах и триггерах и продолжим изучение функций ипроцедур. Вы также познакомитесь с пакетами PL/SQL и увидите примерывзаимодействия с Oracle при помощи программных продуктов других произво-дителей. Говоря конкретнее, вы узнаете, как переносить информацию междубазой данных Oracle и двумя продуктами Microsoft — Access и Excel. Крометого, вы научитесь измерять скорость выполнения программы и определять ко-личество времени, затраченного на завершение процесса. Затем настанетвремя засучить рукава и приняться за написание интересных и полезных про-ектов. В начале этой главы мы обсудим, что требуется для превращения зауряд-ного кода в блестящую программу. Речь пойдет о соглашениях, используемыхпри написании кода.

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

-- Сценарий для запуска перед выполнением упражнений главы 9.— ======================== PERSON =================================

DROP TABLE plsql!01_person;

CREATE TABLE plsql!01_person (

person_code VARCHAR2(3) PRIMARY KEY,

first_name VARCHAR2(15),last_name VARCHAR2(20),hire_date DATE

CREATE INDEX plsq!101_person_name_indexON plsqll01_person(last_name, first_name);

ALTER TABLE plsql!01_person

ADD CONSTRAINT plsqll01_persorv_unique UNIQUE (first_name,

last_name,hire_date

INSERT INTO plsql!01_person VALUES

CCA', 'Charlene', 'Atlas', 'Ol-FEB-02') ;INSERT INTO plsqll01_person VALUES

CGA1, 'Gary', 'Andersen', 45-FEB-02' ) ;

INSERT INTO plsql!01_person VALUES

('BB', 'Bobby1, 'Barkenhagen', '28-FEB-02')

INSERT INTO plsql!01_person VALUES

Другие средства PL/SQL 295

('LB', 'Laren', 'Baxter', ' Ol-MAR-02 ') ;

INSERT INTO plsqH01_person VALUES (

'LN', 'Linda1, 'Norton', 'Ol-JUN-03');

— ================= PRODUCT ========================

DROP TABLE plsql!01_product;CREATE TABLE plsql!01_product (

product_name VARCHAR2(25) PRIMARY KEY,

product_price NUMBER(4,2),

quantity_on_hand NUMBER(5,0),

last_stock_date DATE

ALTER TABLE plsql!01_product ADD (

CONSTRAINT positive_quantity CHECK(

;quantity_on_hand IS NOT NULL

.' AND

quantity_on_hand >= 0'

INSERT INTO plsql!01_product VALUES

('Small Widget', 99, 1, ' 15-JAN-03 ' ) ;

INSERT INTO plsql!01_product VALUES

( 'Medium Wodget', 75, 1000, ' 15-JAN-02 ' ) ;

INSERT INTO plsql!01_product VALUES

('Chrome Phoobar', 50, 100, ' 15-JAN-03 ' ) ;

INSERT INTO plsql!01_product VALUES

('Round Chrome Snaphoo', 25, 10000, null);

INSERT INTO plsq!101_product VALUES('Extra Huge Mega Phoobar +', 9.95, 1234, ' 15-JAN-04 ' ) ;

INSERT INTO plsql!01_product VALUES ('Square Zinculator1,

45, 1, TOJ3ATE (' December 31, 2002, 11:30 P.M.',

'Month dd, YYYY, HH:MI P.M.')

INSERT INTO plsql!01_product VALUES (

'Anodized Framifier', 49, 5, NULL) ;

INSERT INTO plsql!01_product VALUES (

'Red Snaphoo', 1.95, 10, ' 31-DEC-01 ' ) ;

INSERT INTO plsql!01_product VALUES (

'Blue Snaphoo', 1.95, 10, '30-DEC-01')

— =================== PURCHASE ========

DROP TABLE plsql!01_purchase;CREATE TABLE plsql!01_purchase (

product_name VARCHAR2(25),

salesperson VARCHAR2(3),

purchase_date DATE,quantity NUMBER(4,2)

296 Глава9

ALTER TABLE plsql!01_purchaseADD PRIMARY KEY (product_name,

salesperson,purchase_date

ALTER TABLE plsql!01_purchase ADD (CONSTRAINT reasonable_date CHECK(

purchase_date IS NOT NULL

ANDTO_CHAR(purchase_date, 'YYYY-MM-DD') >= '2000-06-30'

ALTER TABLE plsq!101_purchaseADD CONSTRAINT plsql!01_purchase_fk_product FOREIGN KEY

(product_name) REFERENCES plsql!01_product;

ALTER TABLE plsql!01_purchaseADD CONSTRAINT plsql!01_purchase_fk_pers'on FOREIGN KEY

(salesperson) REFERENCES plsql!01_person;

CREATE INDEX plsqll01_purchase_product

ON plsql!01_purchase(product_name);

CREATE INDEX plsql!01_purchase_salespersonon plsql!01_purchase(salesperson);

INSERT INTO plsql!01_purchase VALUES •('Small Widget', 'CA', 44-JUL-03', 1);

INSERT INTO plsql!01_purchase VALUES('Medium Wodgef, 'BB', 44-JUL-03

1, 75);

INSERT INTO plsql!01_purchase VALUES('Chrome Phoobar

1, 'GA

1, '14-JUL-03

1, 2);

INSERT INTO plsql!01_purchase VALUES('Small Widget', 'GA', 45-JUL-03', 8);

INSERT INTO plsql!01_purchase VALUES('Medium Wodgef, 'LB', 45-JUL-03', 20);

INSERT INTO plsql!01_purchase VALUES('Round Chrome Snaphoo', 'CA', 46-JUL-03', 5);

INSERT INTO plsql!01_purchase VALUES('Small Widget', 'CA', 47-JUL-03', 1)

UPDATE plsql!01_productSET product_price = product_price * .9WHERE product_name NOT IN (

SELECT DISTINCT product_name

Другие средства PL/SQL 297

FROM plsql!01_purchase

-- =========================== OLD_ITEM

DROP TABLE plsql!01_old_item;CREATE TABLE plsql!01_old_item (

item_id CHAR(20),item_desc CHAR(25)

INSERT INTO plsq!101_old_item VALUES

CLA-1011, 'Can, Small');

INSERT INTO plsq!101_old_item VALUES

('LA-102', 'Can, Large');INSERT INTO plsq!101_old_item VALUES

CLA-103', 'Bottle, Small');INSERT INTO plsqll01_old_item VALUES

('LA-104', 'Bottle, Large');INSERT INTO plsq!101_old_item VALUES

ONY-101, 'Box, Small');INSERT INTO plsql!01_old_item VALUES

('NY-1021, 'Box, Large

1);

INSERT INTO plsq!101_old_item VALUES('NY-ЮЗ', 'Shipping Carton, Small');

INSERT INTO plsq!101_old_item VALUES('NY-104', 'Shipping Carton, Large

1);

— ========================== PURCHASE_ARCHIVE ======================

DROP TABLE plsq!101_purchase_archive;CREATE TABLE plsql!01_purchase_archive (

product_name VARCHAR2 (25) ,

salesperson VARCHAR2(3),purchase_date DATE,quantity NUMBER (4, 2)

NSERT INTO plsq!101_purchase_archive VALUES('Round Snaphoo', 'BB', '21-JUN-01', 10);

INSERT INTO plsql!01_purchase_archive VALUES('Large Harflinger ' ,' 'GA', '22-JUN-01', 50)

INSERT INTO plsql!01_purchase_archive VALUES

('Medium Wodget', 'LB', '23-JUN-01', 20);INSERT INTO plsqll01_purchase_archive. VALUES

('Small Widget', 'ZZ', '24-JUN-02', 80);

INSERT INTO plsql!01_purchase_archive VALUES('Chrome Phoobar', 'CA', '25-JUN-02', 2);

INSERT INTO plsql!01_purchase_archive VALUES('Small Widget', 'JT', '26-JUN-02', 50);

— ================== Снижение цен (курсор for update) ===

DECLARE

298 Глава 9

CURSOR product_cur ISSELECT * FROM plsqll01_productFOR UPDATE OF product_price;

BEGINFOR product_rec IN product_cur

LOOPUPDATE plsql!01_productSET product_price = (product_rec.product_price.* 0.97)

WHERE CURRENT OF product_cur;END LOOP;

END;

/•'.

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

к выбору шрифтов, расстояний между строками и словами, способов нумера-ции страниц и отображения рисунков. Заголовки всех глав набраны шрифтомопределенного размера, не изменяющегося произвольным образом от главы кглаве. Разумеется, это сделано для того, чтобы создать организованный, легкочитаемый текст. При написании кода хороший программист также будет ис-пользовать стандартные методы форматирования, называемые соглашениями(conventions), чтобы его программу было легче понимать. В этой книге именатаблиц, индексов, функций и процедур выбирались так, чтобы каждое имя повозможности отражало назначение объекта. Например, функция с именемCOMPUTE_DISCOUNTH3 главы 8, как легко понять, предназначена для рас-чета скидок.

Большинство реальных приложений состоят из сотен и тысяч частей кода,или модулей, и могут запросто содержать по нескольку сот тысяч строк кода, аво многих случаях—даже миллионы строк. Обычно в разработке модуля участ-вуют несколько программистов. Код имеет обыкновение постоянно меняться.Разработчики приходят и уходят, при этом новые специалисты должны разби-раться в старом коде и модифицировать его, не прерывая работу с существую-щим приложением. Такое прерывание могло бы привести к полной остановкеважной деятельности, например ежедневных операций крупного финансовогоучреждения. Если каждый разработчик пишет код в своем собственном стиле,подобное развитие событий весьма вероятно, поскольку программу становитсятрудно, а то и вообще невозможно, понимать. Соглашения о кодировании на-столько важны для успеха долгосрочной разработки, что во многих организа-циях созданы специальные отделы и комитеты, занимающиеся исключительновыработкой соглашений и контролем за их соблюдением.

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

Некоторые из соглашений, с которыми вы уже знакомы, состоят в следу-ющем:

• Все команды SQL и PL/SQL записываются в верхнем регистре.

Другие средства PL/SQL 299

• Все имена записываются в нижнем регистре.

• Каждая логически выделенная часть SQL-оператора начинается сновой строки. Например, при выборе нескольких столбцов в оператореSELECT название команды (SELECT) записывается на своей строке,каждое из имен столбцов — на своей, и т.д.

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

• После запятой всегда ставится пробел.

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

Подробнее о взаимодействии PL/SQLи сервера Oracle

Вы уже научились использовать явные курсоры для выборки и модифика-ции данных в таблицах. В этом разделе мы продолжим изучение записейPL/SQL и способов работы с курсорами. Затем вы узнаете о том, как в PL/SQLиспользуются неявные курсоры. В заключение мы рассмотрим, какой кур-сор — явный или неявный — лучше всего подходит в конкретной ситуации.

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

Это похоже на происходящее внутри PL/SQL-процедуры. (Я понимаю, чтопример может показаться натянутым, но положитесь на меня,) Действия пер-вого повара — извлечение продуктов из холодильника, подготовка их к обра^-ботке и передача обработанных компонентов блюд человеку, занимающемусясервировкой, — соответствуют действиям SQL, который извлекает данные изтаблицы, фильтрует, сортирует и передает PL/SQL. Действия второ'го повара —приготовление из сырых продуктов заказанного блюда — соответствуют тому,что делает PL/SQL. Поднос, используемый обоими поварами для передачипродуктов друг другу, аналогичен курсору.

Любое взаимодействие PL/SQL и SQL осуществляется через курсор. Курсор,которому посредством объявления присвоено имя; называется явный (explicit).Курсор, созданный самим PL/SQL для выполнения некоторой операции, на-зывается неявным (implicit). (Подробнее о неявных курсорах будет рассказанониже.)

300 Глава 9

Динамическое объявление типов переменныхи записи PL/SQL

В главе 8 данные из курсора помещались в переменные PL/S QL. В этой главебудет показано, как работать с записями PL/SQL. Как вы уже знаете, записиPL/SQL позволяют собирать разные элементы данных в одно целое и тем са-мым скрывать сложность этих данных. Кроме того, передавать несколько пере-менных менее удобно, чем использовать одну запись, поля которой могутсодержать информацию сразу из всех ваших переменных.

Этот принцип аналогичен принципу контейнерных перевозок, совершив-шему переворот в транспортной индустрии. Каждый контейнер может вме-щать множество товаров, транспортируемых как одно целое. Когда вам нужноизменить ассортимент товаров на складе, достаточно открыть контейнер и по-местить объект внутрь или вынуть наружу. В работе крана, переносящего кон-тейнер с места на место, менять ничего не нужно. Подобно этому,спецификация программного модуля PL/SQL не обязательно должна менятьсяпри изменении структуры записи. Если изменения в записи не влияют на дан-ные, используемые программой, саму программу можно не трогать. Например,если функция принимает запись, но использует только два первых ее поля, этуфункцию не нужно менять при добавлении третьего поля. Однако ее придетсяизменить, если будет удалено первое поле записи. Для реальных приложенийтакая возможность является очень важной и удобной.

В PL/SQL есть другая очень мощная возможность — объявление перемен-ных динамического, или привязанного (anchored), типа, что позволяет автома-тически определять, какие из хранимых программных модулей PL/SQLпотребуют изменения при изменении объектов базы данных, от которых онизависят. PL/SQL будет пытаться автоматически скомпилировать все эти моду-ли, а те из них, которые скомпилировать не удастся, будут отмечены как непри-годные для использования, или недействительные (invalid). Попытавшисьвызвать недействительный программный модуль, вы получите сообщение обошибке. Потом вы сможете модифицировать этот модуль, чтобы привести его всоответствие с изменениями в объектах базы данных. Например, если храни-мая функция использовала запись, основанную на таблице, и вы удалили изтаблицы столбец, который использовался функцией, потребуется переписатьтело функции.

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

• Синтаксис для объявления переменной PL/SQL с типом столбца:

имя_переменнойимя__таблицы.имя__столбца%ТУРЕ;

• Синтаксис для объявления записи с такими же полями, как и в строкетаблицы:

имя_записи

Синтаксис для объявления записи с такими же полями, как и в курсоре:

имя_записи имя_курсора%КОУГГУТЕ;

Другие средства PL/SQL 301

В курсорном цикле FOR используется запись на основе курсора, которуюPL/SQL создает автоматически. Мы только даем ей имя. Запись, использовав-шаяся в главе 8 для снижения цен, называлась product_rec. Ее поля совпадалипо порядку, имени и типу со столбцами, выбранными курсором.

Чтобы создать свою собственную запись, нужно сообщить PL/SQL ее имяи структуру. Это делается в секции объявлений при помощи следующего син-таксиса:

TYPE имя_типа_записи IS RECORD(имя_поля_1 тип_поля_1,имя_поля_2 тип_поля_2,

Фактическое объявление записи имеет вид:

имя_переменной имя_типа_записи

Приведенный ниже пример сводит все сказанное воедино. Создайте этотSQL-сценарий и запустите его в SQL* Plus. См. рис. 9.1.

/* Эффективность работы (performance) продавца — это текущая средняясумма заказа в процентах от исторической средней суммы заказа длятого же продавца. Status возвращает сообщение об ошибках или ихотсутствии.

SET SERVEROUTPUT ONDECLARE

ТУРЕ performance_type IS RECORD(person_code plsq!101_person.person_code%TYPE,person_name plsq!101_person . last_name%TYPE,current_sales NUMBER (8, 2),

perform_percent NUMBER (8,1),status varchar2(30)

one_perform performance_type;

CURSOR person_cur IS

SELECT *FROM plsql!01_person;

/* Эта процедура вычисляет эффективность и текущий суммарныйобъем продаж для одного продавца. Информация о продавцепередается через запись a_person. Если в течение дняпродавец ничего не продал, current_sales устанавливаетсяв нуль. Если история продавца отсутствует (например, онначал работу лишь сегодня) , perform_percent устанавливается

в нуль .

*/PROCEDURE current_performance

(a_person plsql!01_person%ROWTYPE,a_perform OUT performance_type)

302 Глава 9

isCURSOR history_cur (person varchar2) IS

SELECT AVG(tab2.product_price * tabl.quantity)

avg_orderFROM plsql!01_purchase_archive tabl,

plsql!01_product tab2'WHERE tabl.product_name = tab2.product_name

GROUP BY tabl.salespersonHAVING tabl.salesperson = person;

hist_rec history_cur%ROWTYPE;

current_avg_sales NUMBER(8,2) := 0;

BEGIN

a_perform.person_code := a_person.person_code;

a_perform.person_name := a_person.last_name;

a_perform.status := NULL;

BEGINSELECT SUM(tb!2.product_price * tbll.quantity),

AVG tb!2.product_price * tbll.quantity)

INTO a_perform.current_sales,

current_avg_sales

FROM plsql!01_purchase tbll,

plsql!01_product tb!2

WHERE tbll.product_name = tb!2.product_name

GROUP BY tbll.salesperson

HAVING tbll.salesperson = a_person.person_code;

EXCEPTION

WHEN NO_DATA_FOUND

THEN

a_perform.status := 'Current purchases exception';

a_perform.current_sales := 0;

END;

OPEN history_cur (a_person.person_code);FETCH history_cur INTO hist_rec;IF (history_cur%NOTFOUND)

THEN

a_perform.perform_percent := 0;IF (a_perform.status IS NULL)

THEN

a_perform.status := 'Erroneous or no history';END IF;

ELSE

a_perform.perform_percent :=100 * (current_avg_sales.hist_rec.avg_order)/

hist_rec.avg_order;

a_perform.status := 'All fine';END IF;

CLOSE history_cur;

EXCEPTION

WHEN NO DATA FOUND

Другие средства PL/SQL . . 303

•, -.t'a&meaKwwmteM-Wtt&f^:- . - 1Ш л-т1Г1асг :с-„юма-тпЦ1011аюстг;р.. >

THEN ,:. .

a_perform.status := 'Exceptions found';:END current_performance;

BEGIN

FOR person_rec IN person_curLOOP

current_performance(person_rec,.,one_perform);

dbms_output.put_line(one_perform.person_code I !1 1

I Ione_perform.person_name I I

I ' I I ,

one_perform.current_sales I II I

I Ione_perform.perform_percent |

' ' I Ione_perform.status);

END LOOP;

END;

/SELECT * FROM plsql!01_person;SELECT * FROM plsqll01_purchase;

SELECT * FROM plsql!01_purchase_archive;

SELECT * FROM plsqll01_product;

Давайте посмотрим, что происходит в этом сценарии. В самом начале объ-явлен тип записи с именем PERFORMANCEJTYPE. Добавление %TYPE кименам полей, извлекаемых из таблицы PLSQL101_PERSON, гарантирует, чтотипы данных в таблице и процедуре всегда будут совпадать, даже если таблич-ные типы данных в будущем изменятся! Затем объявляется переменная типа за-писи с именем ONE_PERFORM, а следом за ней — простой курсор, которыйвыбирает все строки из таблицы PLSQL101_PERSON.

Процедура CURRENT_PERFORMANCE принимает два параметра. Одинпараметр — это запись о продавце, тип которой совпадает с типом строки таб-лицы PLSQL101_PERSON, а также с типом записей, выбираемых ранее объяв-ленным курсором PERSON_CUR. Таким образом, этой процедуре можнобезопасно передавать записи из курсора PERSON_CUR. ; ;

Второй параметр, A_PERFORM, объявлен как OUT, и это означает, чтопроцедура будет вести в него запись. Процедура содержит явный курсорHISTORY_CUR, соединяющий таблицы PLSQL101_PRODUCT иPLSQL101_PURCHASE_ARCHIVE, чтобы найти среднюю сумму заказа дляданного продавца. Это архивированные (исторические) данные, отсюда имяHISTORY_CUR. На основе этого значения будет определяться, насколькоуспешно продавец выполнял свои текущие продажи. HIST_RBC— это пере-менная типа записи, основанная на курсоре HISTORY_CUR.CURRENT_AVG_SALES — среднее значение по всем текущим заказампродавца.

Выполняемая секция процедуры начинается с кода, который копирует фа-милию продавца и его личный код в выходной параметр A_PERFORM. Затемстатусу присваивается начальное значение NULL.

11 Зак. 725

304 Глава 9

• Я- ni.-irlr- Ч1Л 4'liK НИЕЗ

File £dil Search Options Help

Си ntlas 313.31 7.7 й11 fine -dGft Anderson 865.24 a Erroneous or no historyBB Barkenhagen 5456.25 В Erroneous or no historyLB Baxter 1U55 0 fill fineLN Norton 0 0 Current purchases exception

PL/SQL procedure successfully completed.

SQL> SELECT * FROM plsql101_person;

PER FIHSTJffiME LftST_NftME HIRE_DflTE

Си Charlene Atlas 01-FEB-02GA Gary Anderson 15-FEB-D2BB Bobby Barkenhagen 28-FEB-02LB Laren Baxter 01-HAR-02LN Linda Norton 01-JUN-03

SQL> SELECT » FROM plsqll B1_pur chase ;• • • • ; . . ' ; . . . • •

PRODUCT_NAHE SAL PURCHASE. QUANTITY

Snail Widget CA 14-JUL-B3 1Medium Wodget BB 14-JUL-B3 75Chrome Phoobar GA 11-JUL-B3 2Snail Widget GA 15-JUL-B3 8Medium Wodget LB 15-JUL-83 28Round Chrome Snaphoo CA 16-JUL-B3 5Snail Midget CA 17-JUI-B3 1

• .. ;. i •' , . i •'.-.- • • ' • • '7 rows selected.

SQL> SELECT » FROM plsqll 81_purchase_archiue;

PRODUCT_NAME SAL PURCHASE. QUANTITY

Round Snaphoo BB 21-JUN-B1 18Large Harflinger GA 22-JUN-S1 5BMedium Wodget LB 23-JUN-B1 28Snail Widget ZZ 24-JUN-B2 88Chrome Phoobar CA 2S-JUN-B2 2Snail Widget JT 26-JUN-B2 58

6 rows selected." / • • ' : I ! " ' ' • . '.

SQL> SELECT « FROM plsqll 81_product;

PRODUCT_NAHE PRODUCT_PRICE QUANTITV_ON_HAND LAST_STOC

Snail Widget 96. B3 1 15-JAN-B3Medium Wodget 72.75 1B8B 15-JAN-B2Chrome Phoobar 48.5 188 1S-JAN-83Round Chrome Snaphoo 24.25 108BBExtra Huge Mega Phoobar + 8.69 1234 1S-JAN-B4Square Zinculator 39.29 1 31-DEC-B2Anodized Framifier 42.78 5Red Snaphoo 1.71 18 31 -DEC- 81Blue Snaphoo 1.71 18 38-DEC-81

9 rows selected.

SQL>

JU ^

1

1

,

'.'

!

(

, ' .

N

Рис. 9.1. Пример привязанных, или динамических, типов

Другие средства PL/SQL 305

Далее создается неявный курсор, крторый мы подробнее рассмотрим в сле-дующем разделе. В данном случае неявный курсор'подсчитывает общий объемтекущих продаж и текущую среднюю сумму заказа для продавца, данные о ко-тором получены из входного параметра A_PERSON. Эта часть кода расположе-на в своем собственном базовом блоке, чтобы можно было перехватыватьисключения, возбуждаемые только этим кодом. Как видите, исключение быловозбуждено для Линды Нортон. У нее отсутствуют текущие продажи, поэтомусоответствующие данные не были найдены.

Затем курсор HISTORY_CUR открывается и из него выбираются данные.Заметьте, что произведена единственная выборка, поскольку для одного про-давца может существовать только одна сводная запись. Обратите также внима-ние на использование псевдонима столбца AVG_ORDER. Курсор принимаетличный код продавца в качестве параметра, поэтому код курсора может много-кратно использоваться для разных продавцов. В архиве может не окажется дан-ных для указанного продавца, если он был только что принят на работу. Могутвозникнуть и другие ошибки, в результате которых курсор не вернет ни однойзаписи. В нашем случае таблица PLSQL101_PRODUCT не содержит данных одвух товарах (Large Harflinger и Round Snaphoo). Вот почему сообщение обошибке выглядит как "erroneous or no history", а не просто "no history". Если най-дены верные исторические данные, мы вычисляем процентное отношение те-кущей средней суммы заказа к ее историческому значению и помещаем его вполе A_PERFORM.PERFORM_PERCENT.

В заключение курсор HISTORY_CUR закрывается. Секция исключенийпредназначена для перехвата непредвиденных исключенийNO_DATA_FOUND, которые не были перехвачены ранее.

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

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

Этот законченный пример охватывает много нового материала. Впоследст-вии он будет модифицирован и использован для иллюстрирования некоторыхдругих тем этой главы. Обязательно разберитесь в этом примере, чтобы подго-товить себя к дальнейшему чтению. . , . - , . , . , . . ; •

DML в PL/SQL, или неявные курсорыВ этом разделе мы подробно рассмотрим неявные курсоры, которые были

использованы выше. Взгляните на последний пример. Оператор, определяю-щий текущую среднюю сумму заказа^ представляет собой обычный операторSELECT, дополненный ключевым словом INTO. Это ключевое слово требует-ся для того, чтобы поместить возвращённые оператором значения в соответст-вующие переменные PL/SQL. В нашем примере этими переменными являютсяCURRENT_AVG_SALES и A_PERFORM.CURRENT_SALES. Мы можем ис-пользовать данный оператор SELECT только при условии, что он возвращаетне более одной записи. Если он вернет более одной записи, будет возбуждено"исключение TOO_MANY_ROWS. Для этого оператора PL/SQLиспользует не-

п*

Глава 9

яйный курсор, называемый "SQL". Курсор имеет атрибуты, к которым можнообратиться для получения информации о последней выполненной SQL-опера-ции. Например, атрибут SQL%FOUND сообщит, выбрал ли последний опера-тор SELECT какие-нибудь записи. При наличии двух последовательныхоператоров SELECT информация в SQL%FOUND будет относиться только ковторому из них.

Давайте устраним причины исключений, возбуждавшихся в предыдущемпримере. Сначала вставим данные о продажах для Линды, использовав опера-тор INSERT в коде PL/SQL. Сравните свои результаты с показанными нарис. 9.2.

I £ Oracle SQL'PlusFile Edit Search Options Help

SQL> DECLflRE2 quant NUMBER := 20;3 BEGINt INSERT INTO plsq!1B1 purchase5 VALUES (-Medium Wodget1, •6 'LN1,

•18-flUG-021, i .8 quant);9 IF (SQUNOTFOUND)

10 THEN11 dbms output. put line(' Insert error?*');12 END IF;13 END;1* /

PL/SQL procedure successfully completed.

SQL> SELECT * FROM plsqll B1_purchase ;

PRODUCT НИНЕ SftL PURCHftSE_ QUftNTITV

Small Widget Си 14-JUL-03 1Medium Wodget BB 14-JUL-03 75Chrome Phoobar Си 14-JUL-03 2Small Widget Gfl 15-JUL-03 8Medium Wodget LB 15-JUL-B3 20Round Chrome Snaphoo Си 16-JUL-03 SSmall Widget СЙ 17-JUL-03 1Medium Wodget LN 18-ftUG-B2 20

8 rows selected.

SQL> |

' • • ! - * • " ! • • - ' '"' " •

jJJ . . . . . . . . . . , . . ,

НП-1ЕН1

ij

- ^

\

j.

Рис. 9.2. Вставка записи с использованием PL/SQL

Другие средства PL/SQL 307

DECLAREquant NUMBER := 20;

BEGININSERT INTO plsqll01_purchaseVALUES ('Medium Wodget',

:

'LN' ,48-AUG-02',

quant);IF (SQL%NOTFOUND)

THENdbms_output.put_line('Insert error?!');

END I,F;

END;

/ ;,SELECT * FROM plsql!01_purchase;

Для вставки значения можно использовать переменную PL/SQL. При Этомтип переменной должен либо совпадать с типом столбца, в котором предпола-гается хранить значение, либо допускать преобразование в тип столбца. По-мните, что элементы данных SQL и PL/SQL могут взаимодействовать толькопри совпадении их типов.

Теперь вставим две строки в таблицу PLSQL101_PRODUCT, используяSQL-операторы. Результаты показаны на рис. 9.3.

INSERT INTO plsql!01_productVALUES ( 'Large Harf l inger 1 ,

21,100,'29-AUG-01') ;

INSERT INTO plsql!01_product

VALUES ('Round Snaphoo',12,

144,

'21-JUL-011);

SELECT * FROM plsql!01_product; u. " '•"-.• • ' :••-; • . '- .,-.;.

Теперь обновим эту таблицу из процедуры PL/SQL. См. рис. 9.4.

CREATE OR REPLACE PROCEDURE update_prod (prod_rec plsqll01_product%ROWTYPE

) ISBEGIN

UPDATE plsql!01_productSET last_stock_date = prod_rec.last_stock_date,.

quantity_on_hand = quantity_on_hand+prod_rec.quantity_on_hand

WHERE product_name = prod_rec..product_naine;

END' upda-te_p-rod;/DECLARE ':,•• '. . : , ; : • , , . .

a plsql!01_product%ROWTYPE;

BEGIN

Глава9

END;

;ra..prod\jct_name' := ''Small Widget'

'a'.produet_pri6e v := '87;

a . quantity_on_hand := 31; ,

a.last_Stock_date := TO_DATE,( i'23

update_prod (a) ;

SELECT * FROM plsqll01_product;

Как видите, все довольно просто. При использовании DML-команд SQL вPL/SQL вы должны учитывать все возможные исключения. Это особенно важ-но при использовании неявных курсоров, поскольку они не дают таких воз-можностей контроля, как явные курсоры.

A Oracle SQL'Plus

Be Edit Search Qptions HelpSQL> INSERT INTO plsqll81 product

2 VALUES ('Large Harflinger1.;.„.-,3 21,4 100,5 •29-AUG-011);

1 row created.

SQL>SQL> INSERT INTO plsq!101_product ,

2 UflLUES CRound Snaphoo1,3 12,4 144,5 . т'21-JUL-OI1)!

1 row created.

SQL> SELECT » FROM plsql181_product;

d

PHODUCTJWME PRODUCT_PRICE QUANTITV_ONJWND LAST^STOC

Snail WidgetHediun Wodget ._ ,• :Chrome Phoobar

:

Round Chrome. SnaphooExtra Huge Nega PhoobarSquare ZinculatorAnodized FramiFierRed SnaphooBlue SnaphooLarge HarFlingerRound Snaphoo

1l""rp'iis selected.

SQL>

96.0372.75.48 .524.258.6939.2942.781.711.71

2112

1 15-JAN-031000 15-JAN-02100 15-JAN-03

100001234 1S-JAN-04

1 31-DEC-02510 31-DEC-0110 3Q-DEC-01100 29-AUG-01144 21-JUL-01

Рис. 9.3. Новые строки в таблице PLSQL101_PRODUGT

Другие средства PL/SQL 309

Jf * Oiacle SQL-Plus

file Edit Search Qpliohs Help

SQL> CREATE OR REPLACE PROCEDURE update prod2 prod rec plsql101 product%ROWTVPE3 ) IS4 BEGIN

5 UPDATE plsq!181_product6 SET last_stock_date » prod_rec.

НВЕЭЦД

( л—

last_stock date,7 quantity_on_hand - quantity_on_hand8 + .. . . . ' ' • '9 prod_rec.quantity on_ham|10 WHERE product_name - prod_rec.product_name;11 END update prod;

I 12 /

i Procedure created.

SQL> DECLARE2 a plsq!181 product%ROWTVPE;3 BEGIN4 a.product_name :- 'Small Widget';5 a .product_price := 87;6 a. quantity on hand := 31;

^ ' ' ' ' ' , .

7 a.last_stoch_date :• TO_DATE('23-NOU-01');8 update prod(a);9 END;10 /

PL/SQL procedure successfully completed.

SQL> SELECT « FROM plsq!101_product;

'•'

" t:- ' I

-

PRODUCT_NAME PRODUCT_PRICE QUANTITV_ON_HAND LAST_STOC

Snail Widget 96.03Medium Wodget 72.75Chrome Phoobar 48.5Round Chrome Snaphoo 24.25Extra Huge Mega Phoobar + 8.69Square Zinculator 39.29Anodized Framifier 42.78Red Snaphoo 1.71Blue Snaphoo 1.71Large Harflinger 21Round Snaphoo 12!

J • /' • • !!••

11 rows selected.

SQL>

<U • ' • • • • ' • •

32 23-NOU-011000 15- JAN- 82100 15-JAN-03

180001234 15-JAN-04

1 31 -DEC- 02510 31-DEC-0110 30-DEC-01100 29-AUG-01144 21-JUL-01

iG

.

•• ;

-

Й:'

Рис. 9.4. Обновление таблицы из процедуры PL/SQL с использованиемнеявного курсора

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

ЗНО > Глава9_

-t Oracle SQL-Plus HBO

File Edit Search Options Help'SqL> Set serveroutput oii .±1S(JL> i JSflL> BEGIN f ;; ,

.2.; DELETE FROM plsqliei_product' 3 WHERE product_name = 'junk';

4 IF (SQUHOTFOUND)5 THEN6 dbns_output.put_line('No such product');7 END IF;S END;

c..-fi / ;v<-<H:r;::::

Ho such product ;«.::,(;.

PL/SQL procedure successfully completed.K5 Hv

rii21 ' . * • ':

;. -

]' ' ^ •- - - ^ •: ' -. • . ' ' ' :r: .' :";•..•','. -''''

:' :.'.••'••• • .: - .

SQL> |

JLLJ

Рис. 9.5. Удаление данных из таблицы с помощью PL/SQL-процедуры

set s e r ve r ou{.pijt . on . , , <.. •

BEGINDELETE FROM plsqll01_product .WHERE product_name = 'junk';IF (SQL%NOTFOUND)THEN

dbms_output.put_line ( 'No such product');END IF;

END;

Сравнение явных и неявных курсоровНеявный курсор работает только с одной строкой данных, поэтому в тех слу-

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

Обратите внимание, что курсор HISTORY_CURB примере из предыдущегораздела можно легко заменить неявным курсором, поскольку группированиезаписей по продавцам гарантирует получение только одной строки результа-тов. Далее в этой главе, когда речь пойдёт о помещении функций и процедур впакет, будет показано, как изменить курсор на неявный.

Другие средства PL/SQL , 311

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

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

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

том, чтобы фиксировать время в самой процедуре, как демонстрируется в при-веденном ниже SQL-сценарии. Для определения времени начала и завершенияцикла, вставляющего 5000 записей в таблицу, здесь используется функцияSYSDATE. Затем вычисляется время вставки одной записи путем деления об-щего времени выполнения на 5000. См. рис. 9.6.

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

DROP TABLE plsq!101_timetab CASCADE CONSTRAINTS;

CREATE TABLE plsqll01_timetab (

cl NUMBER NOT NULL,

c2 VARCHAR2(30) NULL,

c3 DATE NULL

CREATE OR REPLACE PROCEDURE test_time IS

maxloops NUMBER := 5000;

loopcount NUMBER (6,0) := 0;

starttime CHAR(5);

endtime CHAR (5) ;

/* Поскольку начальное и конечное время определяются как число

секунд после полуночи, эта процедура не будет работать, если

во время выполнения произойдет переход через полночь .

*/runtime NUMBER;

processrate NUMBER (20, 10) ;BEGIN

starttime := TO_CHAR( SYSDATE, 'SSSSS');

LOOP

loopcount := loopcount +1;

INSERT INTO plsql!01_timetab (Cl, C2, C3)

VALUES (loopcount, 'TEST ENTRY', SYSDATE);

COMMIT;

IF loopcount >= maxloops THEN

312 Глава9

EXIT;

END IF;

END LOOP;

COMMIT;

endtime := TO_CHAR(SYSDATE,'SSSSS');runtime := TO_NUMBER(endtime) - TO_NUMBER(starttime);

dbms_output.put_line(runtime || ' seconds');

processrate := maxloops / runtime;

INSERT INTO plsqll01_timetab (Cl, C2, C3) VALUES

(loopcount+1,

TO_CHAR(processrate, '9999999999') | I

'records per second',

SYSDATE

);

END test_time;

/ : ,EXECUTE test_time;

SELECT * FROM plsql!01_timetab

WHERE cl > 5000;

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

TRUNCATE TABLE plsql!01_timetab;

COMMIT;

SET SERVEROUTPUT ON

BEGIN

FOR trial_count IN 1..10

LOOP

test_time;

COMMIT;

END LOOP;

END;

/SELECT *

FROM plsqll01_timetab

WHERE cl > 5000-ORDER BY c3;

Использование команды TIMINGдля счета реального времени

Когда вам нужно произвести более точные измерения, можно воспользова-ться командой TIMING. Чтобы показать, как это делается, слегка модифици-руем последний пример. Результаты показаны на рис. 9.8. Истекшее времяпредставлено здесь числом 7470. Это означает, что между командами TIMINGSTART и TIMING STOP прошло 7.47 секунды.

TIMING START;EXECUTE test_time;COMMIT;TIMING STOP;

Другие средства PL/SQL 3*3

4" Otacle SOL'Plusfile Edit £eaich Qptions

Table created.

KWI3

SQL>23it56

I9H1112131415161718192*212223212526272829Зв31323334

CREATE OR REPLACE PROCEDURE test_tirae ISnaxloops NUMBER :- 5000;loopcount NUMBER(6,e) := в;starttime CHAR(5) ;endtine CHflR(5) ;/* Note that since the start and end tines are defined in termsof the number of seconds since midnight, this routine will notwork if the run time crosses ouer midnight.

BEGIN

runtime NUMBER;processrate NUMBER (2 8, 18) ;

starttine :- TO CHAR(SVSDATE, 'SSSSS1 ) ;

LOOPloopcount :- loopcount +1 ;INSERT INTO plsqUOl timetab (C1, C2,C3)UALUES (loopcount, 'TEST ENTRY

1. SYSDATE);

COMMIT;IF loopcount >= naxloops THEN

EXIT;'.'•: '. END IF; . f,,r.n: , •.*.•,.•

!Ж ,

M, -;/r. >•.-,-;

END LOOP;COMMIT;endtine := TO_CHflR(SYSDATE, 'SSSSS

1 ) ;

runtime :- TO_NUMBER(endtine)-TO_NUMBEH(starttime);dbms_output.put_line( runtime || ' seconds' );processrate := naxloops / runtime;INSERT INTO plsql101_timetab (C1, CZ, C3) UALUES

(loopcount+1 ,TO_CHAR(processrate, ' 9999999999 ' ) 1 1 ' records per second

1,

SVSDATE

);END test time;

Procedure created.

SQL> EXECUTE test_time;6 seconds

PL/SQL procedure successfully completed.

SQL> SELECT » FROM plsqliei_timetab2 WHERE C1 > 5BOO;

С1 С2

5вЦ

C3. т i • '

833 records per second 22-OCT-OO

SQL>

Рис. 9.6. Измерение скорости выполнения вставок в PL/SQL-программе

314 Глава 9

• * Oiacle SQL-Plus

File £di( Seatch Options Help

SQL> TRUNCATE TABLE plsql181_tinetab;

'Table truncated.

SQL> COMMIT;

Commit complete.

SQL> SET SERUEROUTPUT ONSQL> BEGIN

2 FOR trial count IN 1..183 LOOP4 test time;5 COMMIT;6 END LOOP;

„ 7 END;8 /

7 seconds8 seconds7 seconds'7 seconds7 seconds6 seconds7 seconds8 seconds7 seconds8 seconds

PL/SQL procedure successfully completed.

SQL> SELECT *2 FROM plsql181 timetab3 WHERE C1 > 50804 ORDER BV c3;

C1 C2

5081 714 records per second5001 625 records per second5801 714 records per second5881 714 records per second5881 714 records per second5001 833 records per second5081 714 records per second5801 625 records per second5881 714 records per second5881 625 records per second

18 rows selected.\

SQL>

-dJ

esc

" ' " i!. • • : - . . ГГ ! -V ,• • . . ' . -

• ; , . . ' - • > - . :

- ' " - ' ' . . ' .

.

-- •

. "

•.v -» ..• . , • - -. .;

• ' ' ' ' '. '• . » : ' - . ; " . -

• ' - ' • , ' ' . • • • • . - ' . ' ' • . ' • • ' i

• ' • ; . . • ' . _; - ;;. '4 . : - . • • • , ' ! " ' " . : ' . -•_,

• , " . • -• .

C3

22-OCT-OO22-OCT-OO22-OCT-0822-OCT-8822-OCT-OO22-OCT-8022-OCT-0822-OCT-OO22-OCT-8022-OCT-08

• ' • , • " • ' < ;'• , •" -• s •> .. • • .',-,*. ' ; :'4

• '• : . ' /' - . ' . ' ' : , ' '• 'A

. : V Л

-.*

V

Рис. 9.7. Результаты теста скорости, состоящего из десяти итераций

Другие средства PL/SQL 315

* Oiacle SQL-Plus

£ite £* Search , Options Help •SQL> TIMING STftRT;SQL> EXECUTE test_tine;7 seconds

PL/SQL procedure successfully completed.

SQL> COMMIT;

Commit complete.

SQL> TIMING STOP;real: 7470

SQL>

• •

Рис. 9.8. Измерение скорости INSERT с помощью команды TIMING

Пакеты PL/SQLПочему с персональным компьютером так легко работать? Отчасти потому,

что в нем множество полезных функций объединено в один простой в исполь-зовании пакет. Программное обеспечение показывает экраны, и для достиже-ния желаемого результата не нужно знать, каким образом щелчок мышипередается в компьютер. Сложность "закадровой" деятельности скрыта от по-льзователя, которому достаточно знать лишь о том, что определенное действиес его стороны приведет к предсказуемым результатам. При разработке прило-жений PL/SQL обеспечивает похожий эффективный подход, предоставляяпакеты (packages) для совместного хранения полезных функций, процедур, ти-пов записей и курсоров.

Аналогично процедуре или функции, пакет имеет спецификацию и тело.Однако спецификацию и тело пакета можно создавать по отдельности. Можнодаже заменить одно тело пакета другим, сохранив совместимость с существую-щей спецификацией, и пакет по-прежнему будет работать.

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

Спецификация пакета имеет следующий вид:

CREATE PACKAGE имя_шкета IS[объявления переменных_и_типов]

.« »„, .[спецификациикурсоров][спецификации функций_и_процедур]

END [имя_пакета]',

316 v глава 9

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

CREATE OR REPLACE PACKAGE BODY имя^пакета IS[локальные объявления][полные спецификации курсоров пакета] ,[полные спецификации функций_и_процедур пакета]

BEGIN[выполняемые операторы]

[EXCEPTION][обработчики исключений]

END [имя^пакета]; , .

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

Переменные, присутствующие в спецификации пакета, называются пере-менными пакета (package variables). Они инициализируются только один раз —при первом обращении к нему. Когда производится вызов какой-либо состав-ляющей пакета, Oracle загружает пакет в память, где он остается все то время,пока пользователь соединен с базой данных. При наличии нескольких сеансовпеременные пакета и их значения становятся разделяемыми; следует также за-метить, что обращение к объектам, помещенным в пакет, в последующих сеан-сах может происходить быстрее.

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

Для обращения к процедурам, переменным и функциям пакета использует-ся та же самая нотация контейнер.содержимое. В данном случае она имеет видимя_пакета.объект_пакета. Вы уже использовали одну функцию из пакета —это была функция dbms_output.put_line из пакета dbms_output, поставляемогоOracle. При ссылках на функции внутри их собственного пакета нет необходи-мости помещать квалификатор имя_пакета перед именем функции.

Полагаю, что теперь вы готовы к созданию собственного пакета. Вам пред-стоит взять некоторые из предыдущих примеров и превратить их в пакет. Запу-стите приведенный ниже SQL-сценарий, чтобы увидеть, как создаются ииспользуются пакеты. На рис. 9.9 показаны выходные данные SQL*Plus.

CREATE OR REPLACE PACKAGE plsqll01_pack IS ''•'•'•

DATE_LOADED DATE; . - ••

/* Эффективность (performance) продавца — это текущая средняя

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

для того же продавца. Status возвращает сообщения об ошибках

или их отсутствии.

Другие средства PL/SQL 317

\*/TYPE pkg_perform_type IS RECORD

(person_code plsql!01_person.person_code%TYPE,

per son_name char ( 12 ) ,current_sales NUMBER(8,2),

perform_percent NUMBER(8,1),status char (30)

);CURSOR PKG_PER_CUR RETURN plsql!01_person%ROWTYPE;/* Расчет скидки на заказ.

, Входной параметр — сумма заказа.Возвращается сумма скидки (нуль при неверных входных данных) .

*/

FUNCTION pkg_comp_discounts (order_amt NUMBER) RETURN NUMBER*

/* Эта процедура вычисляет эффективность и текущий суммарныйобъем продаж для одного продавца . Информация о продавцепередается через запись a_person. Если в течение дня продавец

ничего не продал, current_sales устанавливается равной нулю.

Если история продавца отсутствует (например, он начал работулишь сегодня), perform percent устанавливается равной нулю».

!!

*/PROCEDURE pkg_compute_perform

(a_person plsqll01_person%ROWTYPE, .,,-,.,

a_perform OUT pkg_perform_type) ; .,.,END plsql!01_pack;

CREATE OR REPLACE PACKAGE BODY plsql!01_pack IS

small_order_amt NUMBER (8, 2) := 400;large_order_amt NUMBER (8, 2) := 1000;

small_disct NUMBER (4, 2) := 1;large_disct N UMBER (4, 2) := 5;

CURSOR PKG_PER_CURRETURN plsql!01_person%ROWTYPEIS

SELECT *

FROM plsqllOljperson;-/':..,..,

; , . ,-

FUNCTION pkg_comp_discounts (order_amt NUMBER)RETURN

1 NUMBER IS

BEGINIF (order_amt < large_order_amt

AND"order_amt >- small_order_amt)

THENRETURN (order_amt * small_disct / 100) ;

ELSIF (order_amt >= large_order_amt)

THENRETURN (order_amt * large_disct / 10Q) ;

ELSERETURN (0);

318 Глава 9

END IF;

END pkg_comp_discounts;

PROCEDURE pkg_compute_perform(a_person plsql!01_person%ROWTYPE,

a_perform OUT pkg_perform_type)

IShist_ord_avg NUMBER(8,2) := 0;

current_avg_sales NUMBER(8,2) := 0;

BEGINa_perform.person_code :» a_person.person_code;

a_perform.person_name := a_person.last_name;

a_perform.status := NULL;..--

BEGIN

SELECT SUM(tb!2.product_price * tbll.quantity),

AVG(tbl2.product_price * tbll.quantity)

INTO a_perform.current_sales,

current_avg_sales

FROM plsql!01_purchase tbll,

plsql!01_product tb!2

WHERE tbll,product_name = tb!2.product_name

GROUP BY tbll.salespersonHAVING tbll.salesperson = a_person.person_code;

EXCEPTION

WHEN NO_DATA_FOUND

THENa_perform.status :=

'Current purchases exception';

a_perform.current_sales := 0;END;

BEGIN

SELECT AVG(tab2.product_price * tabl.quantity)

avg_orderINTO hist ord_avg

FROM plsqllOl purchase_archive tabl,

plsql!01_product tab2WHERE tabl.product_name = tab2.product_name

GROUP BY tabl.salesperson

HAVING tabl.salesperson = a_person.person_code;

a_perform.perform_percent :«100 * (current_avg_sales-hist_ord_avg) / hist_ord_avg;

a_perform.status := 'All fine';

EXCEPTION

WHEN NO_DATA_FOUND

THEN

a_perform.perform percent := 0;

IF (a_perform.status IS NULL)THEN

Другие средства PL/SQL 319

a_perform.status :=

'Erroneous or no history1.;

END IF;

END;

EXCEPTIONWHEN NO_DATA_FOUNDTHEN

a_perform.status := 'Exceptions found1;'

END pkg_compute_perform;

BEGIN

/* ДаФа первой загрузки пакета */

DATE_LOADED := SYSDATE;END plsql!01_pack;

DECLARE

BEGIN

one_perform plsql!01_pack.pkg_perform_type;cursale char(8);

disct char(8);perf char(8); ,

dbms output.put line('Code' ||

' ' I I'Last Name

1 I I

' . ' I I'Total Sales' ||1 1

I I'Discounts' ||

' ' I I'Performance%' ||I '" I I'Errors?');

FOR person_rec IN plsql!01_pack.PKG_PER_CURLOOP

plsq!101_pack.pkg_compute_perform(person_rec,one_perform);

cursale := TO_CHAR(one_perform.current_sales);disct := TO_CHAR(plsqll01_pack.pkg_comp_discounts

(one_perform.current_sales));perf := TO_CHAR(one_perform.perform_percent);dbms_output.put_line(one_perform.person_code ||

' ' I Ione_perform.person_name ||I I

. 1 1cursale ||1 ' I Idisct ,| |

END LOOP;

12 Зак. 725

perf

Ione perform.status);

320 Глава9

dbms_output.put_line('Pkg load date seconds ' ||TO_CHAR(plsqll01_pack.DATE_LOADED,

dbms_output.put_line('System date seconds ' ||TO CHAR(SYSDATE, ' SSSSS'));

SSSSS1));

END;

/

* Oracle SOL'Plus mmaEd» Search ptioni И**> •'

: : :;'

: , * V.' 'x'-'d

FOR person_rec IN plsql101_pack.pkg per_curLOOP

plsq!101_pack.pkg_conpute_perf orn(person_rec, one_perform) ;

cursale :- TO_CHAR(one_perforn.current_sales);disct :- TO_CHAR(plsql101_pack.pkg_conp_discounts

(one_perforn.cui*rent_sales));perf :- TO_CHftR(one_pet-forn.perforn_percent);

dbns_output.put_line(one_perforn.person_code | |1 ' IIone_perf oi-n.pei-son_nane 1 1' ' IIcursale 1 1

•FJe19202122232425262728293D31323334353637383940

42434445464748

Code Last Name Total Sales Discounts Performance^ Errors?

disct

perf

END LOOP;one_perforn. status) ;

dbns_output.put line('Pkg load date seconds ' ||TO_CHftR ( plsqii Ol_pack .date.loaded , ' SSSSS ' ) > ;

dbns_output.put_line{' System date seconds ' ||TO_CHflR(SVSDftTE, 'SSSSS'));

END;

СЙGflBBLBLNPkg

AtlasAndersonBarkenhagenBaxterNorton

313.31865.245456.2514551455

08.6524272.812572.7572.75

7.7-58.84446.9

load date seconds 39571

All fineAll fineAll fineAll fineErroneous or no history

System date seconds 39571

PL/SQL procedure successfully completed.

SQL>

-iLJ -

Рис. 9.9. Пример пакета и его использования

Другие средства PL/SQL 321

Обратите внимание, что для курсора PKG_PER_CUR требуется указыватьтип возвращаемого значения. Переменная CHAR имеет исключительно "кос-метическое" назначение: она улучшает вид распечатки.

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

ТриггерыВы уже познакомились с триггерами в главе 8. Сначала мы повторим этот

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

няется при возникновении некоторого события, указанного в определениитриггера, — триггерного события (triggering event).

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

• Применение оператора DML к определенному объекту схемы

• Выполнение оператора DDL внутри схемы или базы данных

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

Между триггерами и процедурами PL/SQL есть три различия:

• Триггеры нельзя вызывать из кода программы. Oracle вызывает ихавтоматически в ответ на определенное событие.

• Триггеры не имеют списка параметров.

• Спецификация триггера немного отличается от спецификациипроцедуры.

Сходство между триггерами и процедурами состоит в следующем:

• Тело триггера выглядит точно так же, как и тело процедуры — и то, идругое суть базовые блоки PL/SQL.

• Триггер не возвращает никаких значений.

• Триггеры можно использовать для выполнения разнообразных задач.

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

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

12*

322 Глава 9

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

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

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

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

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

• Триггеры могут собирать статистику доступа к таблицам. Например,триггер позволяет зафиксировать, сколько раз в течение днявыполнялись запросы на "Small Widget" к таблице с каталогом товаров.

Как вы уже, вероятно, поняли, триггеры представляют собой весьма мощ-ные инструменты. Однако их следует использовать аккуратно, поскольку триг-гер может срабатывать при каждом обращении к таблице, тем самымувеличивая нагрузку на сервер базы данных. Разумное практическое правилосостоит в том, чтобы использовать триггер только для действий, которые нель-зя выполнить другими средствами. Например, если есть возможность создатьодно или несколько ограничений, выполняющих работу некоторого триггера,следует использовать эти ограничения. Если итоговые величины допустимовычислять в нерабочее время, систему лучше спроектировать именно так, а неиспользовать триггеры, срабатывающие при каждой транзакции. Предполо-жим, например, что банк принимает решение о доставке дополнительной на-личности из центрального хранилища в зависимости от того, сколько20-долларовых банкнот было выдано за текущий день. Эту величину можноопределять по окончании рабочего дня. Если же для обновления счетчика20-долларовых банкнот будет использоваться триггер, то его постоянное сраба-тывание может существенно замедлить скорость выполнения транзакций в ра-бочие часы.

Триггеры, определенные для таблиц, называются табличными триггерами(table triggers). Далее речь пойдет именно о них. Синтаксис создания триггераимеет следующей вид:

CREATE OR REPLACE ТШССЕКимя_триггерамомент_срабатываниятриггерное_событие

ON имя_таблицы[WHEN триггерное_ргрантение][FOR EACH ROW]

Другие средства PL/SQL 323

[DECLAREобъявления]

BEGINоператоры

[EXCEPTIONWHEN имя_исключенияTHEN...]

END имя_триггера',

В следующих разделах вы увидите, как создавать триггеры, используя этотсинтаксис.

Типы триггеровМомент_срабатывания определяет, когда будет срабатывать триггер: до

(BEFORE) или после (AFTER) наступления триггерного события (выполнениязапускающего оператора). Если указано значение BEFORE, триггер выполня-ется до каких-либо проверок ограничений на строки, затрагиваемые триггер-ным событием. Никакие строки не блокируются. Триггер этого типаназывается, соответственно, BEFORE-триггером (BEFORE trigger).

Если выбрать ключевое слово AFTER, то триггер будет срабатывать послетого, как запускающий оператор завершит свою работу.и будут выполненыпроверки всех ограничений. В этом случае затрагиваемые строки блокируютсяна время выполнения триггера. Триггер этого типа называется AFTER-тригге-ром (AFTER trigger).

Триггерное_событие может принимать значения INSERT, UPDATE'илиDELETE.

Триггерное^ограничение — это одно и более дополнительных условий, кото-рые должны быть выполнены для срабатывания триггера.

Необязательный набор ключевых слов FOR EACH ROW указывает на необ-ходимость выполнить тело триггера для каждой строки, затрагиваемой запус-кающим оператором. Такие триггеры называются строчными (row triggers).Если опция FOR EACH ROW отсутствует, то при наступлении триггерного со-бытия триггер выполняется только один раз. В этом случае он называется опе-раторным триггером (statement trigger), поскольку выполняется только одинраз для каждого запускающего оператора.

Часть кода между DECLARE и END имя_триггера представляет собойобычный базовый блок PL/SQL.

Различные триггерные события можно комбинировать с помощью операто-ра OR. Например:

DELETE OR INSERT остальные_операторы

В случае использования UPDATE можно указать список столбцов:

UPDATE ОРстолбец_1, столбец_2,...

При использовании UPDATE в операторах PL/SQL обращение к новой истарой строкам выполняется с помощью слов "new" и "old", предваренных двое-точием. Так, :оЫ.имя_столбца даст значение, которое столбец имел до обновле-ния. Однако в триггерном ограничении имена "old" и "new" используются бездвоеточий.

324 Глава9

Пример триггераТеперь вам предстоит создать триггер, отслеживающий действия торговых

агентов. Соответствующий сценарий приведен ниже. Результат его выполне-ния показан на рис. 9.10.— Добавить столбец к таблице покупок. Исходное значение — NULL.

ALTER TABLE plsql!01_purchaseADD ORDER_NUMBER NUMBER(10);

— Создать таблицу аудита.CREATE TABLE plsql!01_audit

(ORDER_NUMBER " NUMBER(10),person_code VARCHAR2(3),user_name CHAR(30),user_machine CHAR(20),change_in_quant NUMBER(5),transaction_time DATE,FOREIGN KEY (person_code) REFERENCES plsql!01_person);

— Последовательность для нумерации заказовCREATE SEQUENCE order_num_seq;

CREATE OR REPLACE TRIGGER audit_triggerBEFORE INSERT OR UPDATE ON plsql!01_purchaseFOR EACH ROWDECLARE

no_name_change EXCEPTION;quant_change NUMBER(5) := 0;

BEGIN/* He разрешать изменение product_name в заказах.

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

*/IF (UPDATING

AND(:NEW.product_name <> :OLD.product_name))

THENRAISE no_name_change;

END IF;

/* Создать номер заказа для старых ненумерованных заказов,а также для новых заказов, не имеющих номеров.

*/IF (((UPDATING)

AND(:OLD.ORDER_NUMBER IS NULL))OR((INSERTING)AND(:NEW.ORDER_NUMBER IS NULL)))

THENSELECT order_num_seq.NEXTVALINTO :NEW.ORDER_NUMBERFROM dual;

END IF;

Другие средства PL/SQL 325

/* В заключение занести в таблицу аудита имя пользователя,

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

*/

IF (UPDATING)THEN

quant_change := : NEW. quantity-: OLD. quantity;ELSE

quant_change := : NEW. quantity;END IF;

INSERT INTO plsql!01_auditVALUES ( : NEW . ORDER_NUMBER,

: NEW . salesperson,USER,

USERENV ( ' TERMINAL ' ) ,quant_change,

SYSDATE) ;

EXCEPTION

WHEN no_name_changeTHEN

dbms_output . put_line

('Change of product name not allowed');dbms_output . put_line

('Aborting and resetting to old values');

:NEW.product_name := :OLD.product_name;:NEW. salesperson := :OLD. salesperson;:NEW.ORDER_NUMBER := : OLD.ORDER_NUMBER;

: NEW. quantity := : OLD. quantity;END audit_trigger;

SELECT * FROM plsql!01_purchase;SELECT * FROM plsql!01_audit;

INSERT INTO plsql!01_purchaseVALUES ('Round Snaphoo', 'LN', ' 15-NOV-02 ' , 2, NULL) ;

SELECT * FROM plsql!01_purchase WHERE salesperson = 'LN';

SELECT * FROM plsql!01_audit;

UPDATE plsql!01_purchase SET salesperson = 'LB'WHERE salesperson = 'CA' AND quantity = 1;

SELECT * FROM plsq!101_j>urchase WHERE salesperson =.'CA';

SELECT * FROM plsqll01~audit;UPDATE plsql!01_purchase SET quantity = 20

WHERE salesperson = 'BB';SELECT * FROM plsql!01_purchase WHERE salesperson = 'BB';

SELECT * FROM plsql!01_audit;UPDATE plsql!01_purchase SET product_name = 'Round Snaphoo'

WHERE salesperson = 'BB';SELECT * FROM plsq!101_purchase WHERE salesperson = 'BB';

SELECT * FROM plsq!101_audi.t;

326 ГлаваЭ

£fc. £<Й Seech fiptiorn НфSQL> SELECT * FROM plsqllB1_purchase WHERE salesperson - 'LN';

PRODUCTJMHE SAL PURCHASE. QUANTITV ORDER_NUMBER

Medium Wodget LN 18-AUG-82 28Round Snaphoo LN 15-NOU-G2 2 12

SQL> SELECT • F R O M plsqll01 audit;

ORDER_NUMBCR PER USER_NAHE USER MHCHINE CHANGE IN QUHNT TRANSACTI

12 LN PLSQL181 ALPHANERD 2 22-OCT-B8

SQL> UPDATE plsqll81 purchase SET salesperson - ЧВ1

2 WHERE salesperson * 'CO' AND quantity - 1;

2 rows updated.

SQL> SELECT » FROM plsqllB1_purchase WHERE salesperson - ' C A ' ;

PRODUCTJMHE SAL PURCHASE. QUAHTITV ORDER_NUI«ER

Round Chrome Snaphoo CR 16-JUL-03 5

SQL> SELECT » FROM plsqllB1_audit;

ORDERJWHBER PER USER_NAHE USER_MACHINE CHANGE_IN_QUANT TRANSACTI

12 LN PLSQL1B1 ALPHANERD 2 22-OCT-B813 LB PLSQL101 ALPHANERD 8 22-ОСГ-В81* LB PLSQL181 ALPHAHERD 8 22-OCT-8B

SQL> UPDATE plsqllB1_purchase SET quantity - 28 UHERE salesperson - 'BB';

1 row updated. ,

SQL> SELECT » FROM plsqllB1_purchase WHERE salesperson - 'BB';

PRODUCT JHHME SAL PURCHASE. QUANTITY ORDERJWMBER

Medium Wodget BB 1U-JUL-03 28 15

SQL> SELECT * FROM plsqllB1_audit;

DRDER_NUHBER PER USER_NAHE USER_HACHINE CHAHGE_IN_QUANT TRANSACTI

12 LN PLSQL1B1 ALPHANERD 2 22-OCT-BB13 LB PLSQL1B1 ALPHANERD 8 22-OCT-B814 LB PLSQL1B1 ALPHANERD 8 22-OCT-D815 BB PLSQL1B1 ALPHANERD -55 22-OCT-88

SQL> UPDATE plsq!181 purchase SET product_nane - 'Round Snaphoo'2 WHERE salesperson - 'BB';

Change OF product name not allowedAborting and resetting to old values

1 row updated.

SQL> SELECT « FROM plsqllB1_purchase UHERE salesperson - '88';

PRODUCT_NAHE SAL PURCHASE. QUANTITV ORDER_HUMBER

Hediun Wodget BB Ht-JUL-03 2B 15

SQL> SELECT » FROM plsqllB1_audit;

ORDER_HUH8ER PER USER_NANE USER_MACHINE CHAHGE_IN_QUANT TRANSACTI

12 LN PLSQL1B1

13 LB PLSQL181

14 LB PLSQL1B1

15 BB PLSQL1B1

ALPHANERD

ALPHANERD

ALPHANERD

ALPHANERD

2 22-ОСТ-ев

8 22-OCT-BB

8 22-OCT-BB

-55 22-OCT-88

SQL>

Рис. 9.10. Триггер, запрещающий изменение названия товарапри обновлении заказов

Другие средства PL/SQL 327

Давайте поговорим о том, что здесь происходит. Сначала к таблице покупокдобавляется столбец ORDER_NUMBER. Затем создается таблица для аудита за-писей, содержащая имя вошедшего в систему пользователя. Имя пользователявводится с помощью функции USER, а имя его компьютера или терминала —с помощью функции USERENV, вызываемой с параметром TERMINAL1. Так-же вводятся личный код продавца, изменение в количестве заказанного товара,номер заказа и дата изменения. Триггер не позволяет изменятьproduct_name иавтоматически генерирует номера для новых заказов, а также старых записей втаблице заказов, которые не были пронумерованы. При успешном обновленииили вставке триггер вставляет значения в таблицу аудита. Ключевые словаUPDATING и INSERTING сообщают нам, что послужило запускающим дей-ствием — обновление или вставка.

Сначала триггер был протестирован путем вставки строки в таблицуPLSQL101_PURCHASEn сравнения старых значений с новыми. Затем быливыполнены два простых обновления, а в заключение — попытка изменить на-звание товара в заказе.

Модификация триггеровДля триггеров, как и для представлений, не существует команды модифика-

ции. Старый триггер просто заменяется новым с помощью команды CREATEOR REPLACE TRIGGER.

Триггер можно удалить, выдав команду со следующим синтаксисом:

DROP TRIGGER имя_триггера;

Существующие триггеры можно деактивизировать и повторно активизиро-вать, используя команды с таким синтаксисом:

ALTER TRIGGER имя_триггера DISABLE;ALTER TRIGGER имя_триггера ENABLE;

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

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

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

• В триггере, запускаемом оператором UPDATE, можно обращаться кстарым и новым значениям столбцов. Это касается как BEFORE, так иAFTER-триггёров.

• В триггере, запускаемом оператором DELETE, имеет смыслобращаться только к старым значениям столбцов. Поскольку удаленнаястрока перестает существовать, новым значением всегда будет NULL.Однако значения :new нельзя модифицировать. Если вы попытаетесьэто сделать, будет выдано сообщение об ошибке ORA-4084.

328 Глава 9

• В теле триггера нельзя использовать операторы ROLLBACK, COMMITи SAVEPOINT.

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

• Если для одного триггерного события определено более одноготриггера, то порядок их срабатывания не определен, т.е. вы не можетезаранее сказать, в какой последовательности они будут срабатывать.

• Когда триггер пытается прочитать таблицу, а затем записать в нееданные, возбуждается исключение "мутирующей таблицы". Хотянамерение ограничить действия такого типа вполне объяснимо,корпорация Oracle зашла в этом дальше, чем необходимо, запретивоперации, при которых таблица просто блокируется, а не изменяется.Один из способов обойти эту проблему заключается в том, чтобысоздать вторую таблицу с копиями столбцов, которые триггер долженпрочитать из главной таблицы. В этом случае триггер получаетнеобходимые значения из второй таблицы, а затем выполняетзапланированные действия с первой таблицей. При реализацииданного подхода важно обеспечить синхронизацию таблиц. Для этоговсе операции INSERT, UPDATE и DELETE над главной таблицейдолжны отражаться триггером на второй таблице.

ODBCДо сих пор все ваше взаимодействие с Oracle в рамках этой книги осуществля-

лось через программу SQL*Plus. Для некоторых типов задач ничего другого и нетребуется, но возможны и иные ситуации, когда было бы предпочтительнее рабо-тать с данными Oracle из программ типа Microsoft Access или Excel. Это легко сде-лать с помощью соединения специального типа, о котором еще не говорилось, икоторое не слишком хорошо документировано. Имеется в виду ODBC-соедине-ние. В этом разделе показано, как создать такое соединение в Windows.

ODBC расшифровывается как "Open Database Connectivity" ("открытыесредства связи с базами данных"). Это интерфейс, позволяющий приложениямтипа Access или Excel взаимодействовать со многими различными СУБД уни-версальным способом. Уровень ODBC позволяет программам использоватьсредства связи с Oracle, которые применяются в SQL*Plus. На приведеннойниже иллюстрации показано место ODBC в общей картине соединений.

Создание ODBC-соединенияДля создания ODBC-соединения необходимо знать четыре вещи:

• Марку СУБД

• Имя базы данных

• Действительное имя пользователя

• Действительный пароль

Другие средства PL/SQL 329

Если вы делали упражнения этой или любой другой главы, то знаете все че-тыре пункта. Марка СУБД — это Oracle, а имя базы данных, имя пользователя ипароль вводились в диалоговом окне при входе в SQL*Plus. На рис. 9.11 показанодиалоговое окно входа и отмечено, какие поля каким пунктам соответствуют.

User Name:

Password:

Host Suing:

IPLSQLIOI

OK

|your_ho8t_name|

Cancel

Рис. 9.11. Информация о соединении SQL'Plus, используемаяпри создании ODBC-соединения

Чтобы создать ODBC-соединение, выполните следующие действия:

1. Запустите Microsoft ODBC Administrator. Для этого откройте меню Start,перейдите в раздел Programs и выберите программную группу Oracle.Найдите и запустите программу с названием Microsoft ODBC DataSource Administrator. Вы должны увидеть экран, аналогичныйпоказанному на рис. 9.12.

t 'ЦШПС Data Souice Adminisltatoi

UserDSN | S^tomDSN | FileDSN | Drivers | Tracing j .Connection Рос** | About |

Jser Data Sources:

Name | Drivei 1.EJJSSJ2SI Microsoft dBase Driver (".dbf)DeluxeCD Microsoft Access Olivet (*.mdb)Excel Files . Microsoft Excel Drivei |".xls)FoxPioFtes Microsoft FoxPro Driver (".dbf)MS Access 7.0 Database Microsoft Access Oliver (".mdblMS Access 37 Database Microsoft Access Driver (".mdbjParadox Files Microsoft Paradox Driver (N.db)Text Files Microsoft Text Driver ('.bit; ".csvjVisual FoxPro Database Microsoft Visual FoxPro DriverVisual FoxPro Tables Micro«ofl Visual FoxPro Oliver

Ajjd... ,

Bemove

Configure...

yBEfl An ODBC User data source stores informatbn about how to conned tor pJ the indicated data provider. AUser data source is only visible to you.^ ^ end can onfci be used on the current machine.

OK. J Cancel | Apply | Help

.

Рис. 9.12. Microsoft ODBC Data Source Administrator

330 Глава 9

2. Если на вашем компьютере работает несколько человек и вы хотите,чтобы ODBC-соединение было доступно всем пользователям, щелкнитена вкладке System DSN в верхней части окна. Если ODBC-соединениебудет использоваться только вами, оставайтесь на вкладке User DSN.

3. Щелкните на кнопке Add. При этом откроется диалоговое окно CreateNew Data Source, показанное на рис. 9.13. Выберите источник данных сименем Oracle ODBC Driver и щелкните на кнопке Finish.

4. Теперь вы должны увидеть диалоговое окно с заголовком OracleS ODBCDriver Setup, показанное на рис. 9.14. Первое поле называется DataSource Name. Введите туда имя, по которому вы хотели бы ссылаться наданное ODBC-соединение. Для простоты в качестве имениODBC-соединения обычно указывают имя базы данных. Учитывая этурекомендацию, введите в поле Data Source Name строкуPLSQL101.

5. Перейдите к полю Service Name. Введите туда имя базы данных Oracle,которое вы использовали при входе в SQL*Plus. Для этого упражненияследует ввести значение PLSQL101.

6. Щелкните на кнопке ОКдля завершения конфигурирования и возвратак диалоговому окну ODBC Data Source Administrator. В списке DataSources вы должны увидеть только что созданный источник данных.

7. Щелкните на кнопке ОК, чтобы закрыть диалоговое окно ODBC DataSource Administrator.

8. Чтобы протестировать новое ODBC-соединение, вернитесь в папку спрограммами Oracle в меню Start и запустите программу под названиемOracle ODBC Test. Вы увидите экран, аналогичный показанному нарис. 9.15.

Create New Data Source

Delect a driver lor which you want to set up a data source.

Name

Microsoft Excel Driver ('.xls)Microsoft FoxPro Driver f.dbf]Microsoft ODBC lor OracleMicrosoft Paradox Driver (".do)Microsoft Text Driver (".txt; ".csv)Microsoft Visual FoxPro Driver

Oracle ODBC Driver for RdbSQL Server

Рис. 9.13. Создание нового источника данных

Другие средства PL/SQL 331

,

OracleS ODBC Driver Setup

Data Source Name: II

Description:

Service Name: I

UserlD:

ГЧ » U П l'

Connect to database in Read only mode Г"

Prefetch Count: hL '

Enable Thread Safety F

Enable Failover F R etry Count JT5 Delay: fio

т . L- eL&n

flption: fo

|

ВШВ1П<1^ «OK 1

Cancel 1

Help

i

:

.

'

., • '

Рис. 9.14. Конфигурирование ODBC-драйвера Oracle

SOiaclH ODBC 32Bit Test - IQUERY1I

|g Fife Edit yiew tfndow B°wSet

QjagjBl ^Nffll <S|?I „______________I Connect.. I Disconnect I All Tables I User Tables E:«earte... (NextRowSet

u

Ready

Рис. 9.15. Экран тестирования ODBC-соединения

332 Глава9

9. Щелкните на кнопке Connect, и вы увидите диалоговое окно Select DataSource, показанное на рис. 9.16.

10. Щелкните на вкладке Machine Data Source и найдите в алфавитномсписке имя своего ODBC-соединения. Чтобы выбрать это соединение,щелкните на его имени, а затем на кнопке ОК.

11. После появления диалогового окна Oracle ODBC Driver Connectвведите имя пользователя и пароль (заметьте, что имя базы данных ужевведено, причем оно находится в первом поле, а не в последнем, как вSQL*Plus). Для этого упражнения введите в качестве именипользователя и пароля одну и ту же строку PLSQL101. В завершениещелкните на кнопке ОК.

12. Единственным свидетельством успешного прохождения теста будет за-тенение кнопки Connect и активизация кнопок Disconnect, All Tables,User Tables и Execute. Щелкните на кнопке User Tables. Долженпоявиться список таблиц (см. рис. 9.17). По умолчанию программаOracle ODBC Test показывает только по пять таблиц за один раз. Дляувеличения этого числа либо щелкните на кнопке Next Row Set, либовыйдите в меню программы и выполните команду RowSet 1100.

13. Чтобы выйти из программы Oracle ODBC Test, щелкните на кнопкеDisconnect, а затем выполните команду меню File | Exit.

Select Data Source

Fife Data Source: Machine Data Source |

Data Source Name I Type I Description -±JMS Access 7.0 Database UseMS Access 97 Database UseORCL_ODBC SystParadox Files Use{йПВДЦ'К ! SystText Files UseVisual FoxPro Database UseVisual FoxPro Т abtes U se

<i ' ; g j jii . . • • • . - •

em

em

I >ГHew... |

A Machine Data Source is specific to this machine, and cannot be shared."User" data sources are specific to a user on this machine. "System" datasources can be used by all users on this machine, or by a system-wide service.

OK Cancel Help

Рис. 9.16. Выбор источника данных ODBC для тестирования

Другие средства PL/SQL 333

1

;

& № Ь* У»* Wn*>« flowSet belp ,1 1 X)

QloSJHJ ХНЫ®] 45|tf|

Connect. Disconnect All Tables 1 User Tables 1 Execute... Next Row Set 1

• • - • . •:

* ^to* I TBBLE_CflI TflBLE_SCHEM TABLE.MHE

1 <Null> PLSQL181 PLSQL1H AUDIT2 <Null> PLSQL1I1 PLSQL1I1~LOG3 <Mull> PLSQL1I1 PLSQL101~OLD ITEHH <Null> PLSQL101 PLSQLH1 PERSONS <Hull> PLSQL1B1 PLSQUtl .PRODUCT

Rea* .^ :; \ : . . . . : . , . ; i i . . . ; . • . . ! ' : |NUM|:. •:, j

, ' -

,

.

Рис. 9.17. Успешное завершение теста ODBC-соединения

Поздравляю с успешным созданием ODBC-соединения с базой данныхOracle! Теперь рассмотрим, как оно применяется.

Применение ODBC-соединенийВ этом разделе будет показано, как выполняются наиболее распространен-

ные задачи, требующие ODBC-соединения: манипулирование данными Accessи импорт данных в Excel.

Просмотр и редактирование данных AccessПеречисленные ниже шаги позволяют создать связь между таблицами Oracle

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

1. Запустите программу Access. Для целей этого упражнения создайтеновый файл Access с именем PLSQL101.mdb.

2. Выполните команду меню File | Get External Data | Link. Это приведет коткрытию стандартного диалогового окна File Open.

3. Перейдите в поле Files of Type и раскройте список типов файлов.В нижней части списка выберите пункт ODBC Databases. Вы должныувидеть диалоговое окно ODBC Select Data Source (которое былопоказано на рис. 9.16).

334 Глава 9

4. Щелкните на вкладке Machine Data Source. Найдите нужный источникданных ODBC (в этом упражнении — PLSQL101), щелкните на нем, азатем на кнопке ОК.

5. После появления диалогового окна входа введите имя пользователя спаролем и щелкните на кнопке ОК.

6. Вы увидите список таблиц и представлений Oracle, доступных черезданное ODBC-соединение. В этот список включаются объекты,принадлежащие другим пользователям; каждое имя объектапредваряется именем его владельца. Прокрутите список вниз, пока ненайдете свое имя пользователя, и выберите нужные объекты. Для этогоупражнения следует выбрать все объекты PLSQL101. В отличие отбольшинства Windows-программ Access не поддерживает выделение всписке нескольких объектов с помощью сочетаний CTRL+щелчок иSHIFT+щелчок. Для выделения более чем одного объекта достаточновыполнить обычный щелчок на каждом интересующем вас имени.

7. Чтобы не вводить имя пользователя и пароль при каждом использованиисоздаваемой связи таблиц, установите флажок Save Password.

8. Щелкните на кнопке ОК. Access Начнет считывать структурувыбранных таблиц и представлений. Если он встретит таблицу безпервичного ключа, то попросит указать один или несколько столбцов,которые уникально идентифицируют записи этой таблицы. Например,таблица PLSQL101_AUDIT не имеет первичного ключа, поэтому Accessвыведет диалоговое окно, показанное на рис. 9.18. Вы не обязанывыбирать столбцы первичного ключа для таблицы, в которой онотсутствует, но если этого не сделать, таблица будет доступна только длячтения — вы не сможете добавлять или изменять записи, используясвязь Access. Что касается данного конкретного случая, то таблицааудита все равно не должна меняться через Access, поэтому можнощелкнуть на кнопке ОК, не указывая столбцы первичного ключа.

Select Unique Record Identifier

0elds in table 'PLSQL101_PLSQL101_AUDIT':

ORDER NUMBERPE~RSON_Coi5e~USER_NAMEUSER_MACHINECHANGE_IN_QUANTTRANSACTION TIME

To ensure data integrity and to update records, youmust choose a field or Fields that uniquely identify eachrecord. Select up to ten fields.

OK

Cancel

Рис. 9.18. Запрос для идентификации выбора одного или более столбцовпервичного ключа для таблицы, в которой он отсутствует

Другие средства PL/SQL 335

9. Сейчас вы должны увидеть серию записей о таблицах, похожих на те,что показаны на рис. 9.19. Обратите внимание, что к имени каждойтаблицы добавлено имя ее владельца. При желании эти связи можнопереименовать; имена базовых таблиц Oracle не будут затронуты.

10. Чтобы открыть таблицу в Access, дважды щелкните на ее имени.Например, двойной щелчок на связи PLSQL101_PURCHASE приведетк появлению экрана, показанного на рис. 9.20.

Импорт данных Oracle в ExcelВ отличие от Access связь между Excel и Oracle является односторонней: дан-

ные можно считывать из Oracle в Excel, но не наоборот. Это соответствует ти-пичному применению Excel в качестве инструмента анализа.

Чтобы "затянуть" данные Oracle в Excel, выполните следующие действия:

1. Запустите программу Excel.

2. Выполните команду меню Data | Get External Data | Create New Query.Это приведет к появлению диалогового окна Choose Data Source,показанного на рис. 9.21.

3. Найдите нужный источник данных (для этого упражнения —PLSQL101), щелкните на нем, а затем на кнопке ОК.

4. В появившемся диалоговом окне входа введите имя пользователя спаролем и щелкните на кнопке ОК.

5. Вы увидите список таблиц и представлений Oracle, доступных через данноеODBC-соединение. Слева от каждого имени таблицы находится знакплюса; щелчок на нем приводит к раскрытию списка и отображению

• •*, Micfoioll Access - [PLSUL10! : Ualabase) Hl*tE3 •

II© 9» &ft View [nsert lools aM°* H* -IfflX

ie

STables | ^Queries ] Sperms | В Reports | 3 Macros ] «$ Modules j

»ф KSQ1101_PL5Q1101_AUDITJ gl»" |

»ф RSQL101_PL5QL101_OLDJTEM pK)gn j

»H PLSQll01^PLSQI.mi_PERSON

»Ф PLSQI.101J>L5QLmi_PROOUCT У 1

»Ф aSQL101_PLSQL101_PURCMASE

>ф PLSQC101_PLSQL101_PURCHASE_ARCHIVE

>ф PL5QL101J>L5QL101_TIMETAB

1 • .

eed, [ p-f— f ЙЖГ !: X«

Рис. 9.19. Связанные таблицы в Access

336 Глава 9

ull Access -IPLSQL1U1 PLSQL101 PUflCHASE

1 FJe Edit И"» Insert Format Records Idols Window ИФ

v м | »*PRODUCTJIAHE I SALESPERSON | PURCHASE_DATE | QUANTITY | ORDER

Medium Wodget

Chrome Phoobar

Small Widget

Medium Wodget

CABBGA

GALB

Round Chrome Snaphoo CA

Small Widget CA

Medium Wodget LN

7/14/2003

7/14/2003

7/14/2003

7/15/2003

7/15/2003

7/16/2003

7/17/2003

8/18/2002

{DaTisheetVIm

Рис. 9.20. Отображение данных Oracle в Access

Choose Data Source

Databases I Queries | OK

MS Access 97 Database'ORa_ODBC"Paradox Files (not sharable)Paiadox Files*PLSQL101"Тек) Files (not sharable]Text FJes"Visual FoxPro Database-Visual FoxPro Tables"

J

JGp| F Use the Query Wizard to create/edit queries

Cancel

Browse... j

Рис. 9.21. Выбор источника для импорта данных в Excel

столбцов. Можно выбирать все столбцы таблицы или представления,щелкая на имени таблицы или представления, а затем — на стрелке впра-во, расположенной в середине диалогового окна. Можно выбирать отде-льные столбцы, раскрывая список, щелкая на имени нужного столбца, азатем — на той же стрелке вправо. Для целей этого упражнения перемес-тите все столбцы таблицы PLSQL101_PURCHASE в правое окно с заго-ловком Columns in Your Query. Когда экран будет выглядеть так, какпоказано на рис. 9.22, щелкните на кнопке Next для продолжения.

6. Следующее диалоговое окно позволяет указать критерии фильтрации,подобные конструкции WHERE в SQL-команде SELECT. Я уверен, чтолюбой, кто дочитал книгу до этого места, поймет, как использоватьданное окно, поэтому можете просто щелкнуть на кнопке Next дляпродолжения.

Другие средства PL/SQL 337

What columns of data do you want to include in your query?

Available tabtes and columns: Columns in your query:

6! PLSQL101 .PRODUCT jj. >

Я PISQL1 01 .PURCHASE — —Ш PLSQL101 PURCHASE ARCHIVE -J «ffl PLSQL101_PURCHASE_ARCHIVE

Й PLSQL10lIPURCHASE_LOG -J

^review of data in selected column:

PRODUCT NAMESALESPERSONPURCHASE DATEQUANTITYORDER NUMBER

Gp| Pjeviev* Now j •< §ack 1 ^eKt> {

j

Cared |

Рис. 9.22. Выбор столбцов Oracle для импорта в Excel

7. В следующем диалоговом окне предлагается указать до трех столбцовсортировки. Щелкните на кнопке Next.

8. В последнем диалоговом окне мастера запросов выберите опцию ReturnData to Microsoft Excel и щелкните на кнопке Finish.

9. В завершение вы увидите диалоговое окно с вопросом о том, куда следуетпоместить данные из Oracle. Щелкните на кнопке ОК.

10. Теперь данные Oracle должны находиться в таблице Excel, как показанона рис. 9.23.

JpFJe £* Bew insert Format tools E*a Window Help

]|D а* в [sa У *lajJArial - 10 '||H

Al _J . »

1 A1 PRODUCT1 NAME SAL

2 Small Widget CA

3 Medium Wodget BB

A Chrome Phoobar GA

5 Small Widget GA

6 Medium Wodget LB

7 Round Chrome Snaphoo CA

8 Small Widget CA

9 Medium Wodget LN

101112

13 ватятяят-Ц- :9&tk\ 1 Ж ®

16,17Jt М>|Л S**MI / — ' '

Ready

-iJlx't

ft j ю , o. . | 4 «f | Z /. Й' "iiHl f Г© " jj^ :

7 II •* » S Ш | » % i « Л

8 J С J D J_

*| E- *•• Д,г '•

E 1 RTESPERSON PURCH"ASE DATE QUANTITY ORDER NUMBER

7/14/20030:00 1

7/1 4/2003 0:00 75

7/14/20030:00 2

7/1 5/2003 0:00 8

7/15/20030:00 20

7/16/2003 0:00 5

7/17/20030:00 1

8/18/20020:00 20

•x]

i

~~ i < i

,

• •

- '

-1

j, - 1 >1Г

№м| !Г~~Г~~ /s

Рис. 9.23. Данные Oracle, импортированные в Excel

338 Глава 9

ИтогиЭта глава началась с обсуждения соглашений о кодировании, которые слу-

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

Вы продолжили изучение неявных курсоров — иначе говоря, DML SQL вPL/SQL или SQL-курсорах. Неявные курсоры полезны, когда строки обраба-тываются строго по одной. Они не разделяются между сеансами. Неявные кур-соры позволяют выбирать значения непосредственно в переменные PL/SQL, aтакже использовать переменные PL/SQL в конструкции WHERE.

Вы подробнее изучили записи PL/SQL и узнали, как объявлять переменныепривязанных типов. %ROWTYPE и %TYPE позволяют привязывать тип запи-си или переменной к типу строки конкретной таблицы или курсора, или к типустолбца таблицы.

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

Мы рассмотрели пакеты PL/SQL, которые позволяют скрывать детали реа-лизации PL/SQL-модулей, создавая тем самым более прозрачные интерфейсыпрограммирования. Код пакета может разделяться между многими сеансами.Пакет имеет видимую извне спецификацию, в которой раскрыты все его до-ступные части, такие, как функции и процедуры.

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

В заключение вы создали ODBC-соединение со своими таблицами Oracle.Настроить ODBC-соединение несложно; нужно лишь знать, какая информа-ция для этого требуется. Установленное ODBC-соединение может с легкостьюиспользоваться продуктами Microsoft: Access или Excel.

Поздравляю! Теперь вы знаете PL/SQL лучше, чем большинство людей, скоторыми будете встречаться. Желаю вам успеха!

Вопросы1. Что из сказанного ниже относительно DML SQL в PL/SQL справедливо?

A. Реализовать DML SQL можно двумя способами: при помощи явныхкурсоров и неявных курсоров.

B. DML SQL нельзя реализовать в коде PL/SQL.

C. Явные курсоры нельзя использовать для выполнения операторовDML, затрагивающих не более одной строки.

Другие средства PL/SQL 339

D. Переменные PL/SQL можно использовать в DML SQL внутри кодаPL/SQL.

2. Соглашения о кодировании важны поскольку:

A. Следование им облегчает сопровождение программного обеспечения.

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

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

D. Они гарантируют переносимость любого кода.

3. Что из сказанного ниже относительно пакетов неверно?

A. Пакеты могут содержать триггеры.

B. Переменные пакета — это переменные, объявленные в, спецификации пакета и инициализируемые только один раз, при

загрузке пакета в память.

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

D. Функции и процедуры, объявленные и определенные в теле пакета,но не объявленные в его спецификации, недоступны пользователямпакета.

4. Что из сказанного ниже относительно триггеров справедливо?

A. Триггеры могут вызываться системой автоматически.

B. Триггеры могут вызывать другие хранимые процедуры или функцииPL/SQL.

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

D. Можно писать триггеры для пакетов, поскольку пакеты такжехранятся в базе данных.

Ответы на вопросы1. A, D.

Объяснение Разумеется, В неверно; две последние главы содержалимногочисленные примеры, демонстрирующие использование командDML в коде PL/SQL. С неверно, поскольку явные курсоры могутвозвращать единственную строку или вообще не возвращать строк.

2. А, В и С.

340 Глава 9

Объяснение А, В и С могут служить основаниями дляиспользования соглашений о кодировании, тогда как D — нет. Хотя выможете разработать соглашения о кодировании и именовании,обеспечивающие перенос кода на многие платформы, одни толькосоглашения не гарантируют переносимости. Это обусловленозначительными различиями аппаратных и программных платформ.

3. А. Пакеты могут содержать триггеры.

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

4. А, В и С.

Объяснение D неверно. Oracle не позволяет писать триггеры,срабатывающие в ответ на события пакета — по той же причине, покоторой триггеры нельзя помещать внутри пакетов.

,

Глоссарий

:

342 Глоссарий

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

after-триггер (after trigger) Триггер, срабатывающий после того, кактриггерный оператор завершит свою работу и будут проверены все ограниче-ния. Этот триггер блокирует строки, затронутые соответствующим триггер-ным оператором.

before-триггер (before trigger) Триггер, срабатывающий до выполнениятриггерного оператора и проверок каких-либо ограничений.

null Индикатор отсутствия данных. Его не следует путать с нулем или пробе-лом, которые являются реальными значениями. Null означает, что значениевообще неизвестно.

ODBC Open Database Connectivity (открытые средства связи с базами дан-ных). Протокол промышленного стандарта, предназначенный для установле-ния соединения и передачи информации между базой данных и клиентскимпрограммным обеспечением.

PL/SQL Процедурное расширение SQL. Язык программирования, предо-ставляемый Oracle.

анонимный блок (anonymous block) Базовый блок PL/SQL без имени испецификации, с пустой секцией заголовка.

атрибуты курсора (cursor attributes) Свойства курсора, показывающие,содержит ли он строки, сколько строк выбрано, открыт он или нет и была липолучена какая-нибудь строка в результате последней выборки.

база данных (database) Совокупность связанных таблиц.

базовый блок (basic block) PL/SQL Основной структурный компоненткода PL/SQL. Состоит из секции заголовка, секции объявлений, выполняемойсекции и секции исключений, которые могут компилироваться как самостоя-тельная единица.

блоки-ветви (branch blocks) В индексе В*-дерева — блоки, не являющие-ся блоками-листьямии не содержащие фактических записей. Они содержатто-лько значения индексированных полей и помогают достигать блоков-листьевза минимально возможное число шагов.

блоки-листья (leaf blocks) При построении индекса В*-дерева Oracle ана-лизирует столбцы таблицы, после чего разбивает таблицу на единицы хране-ния, называемые блоками. Каждый блок содержит одинаковое число записей.В индексе эти блоки называются блоками-листьями.

буферизация (spooling) Процесс записи экранной информации в диско-вый файл.

Глоссарий 343

вложенные блоки, вложенные циклы (nested blocks, nested loops)Блоки (циклы) PL/SQL, помещенные внутри других блоков (циклов).

внешний ключ (foreign key) Столбец (столбцы) подчиненной таблицы созначениями первичного ключа главной таблицы.

возбуждение исключения (raising exception) Уведомление о возникно-вении исключительной ситуации, направляемое среде PL/SQL или системе.Это делается явно (с помощью оператора RAISE в коде программы) или неяв-но, самой системой.

выражение (expression) Программная конструкция, являющаяся частьюкоманды и вырабатывающая одно или несколько значений. Например: 3 + 4или sales_tax / product.

вычисления с датами (date math) Арифметические операции над дата-ми — например, определение количества дней между двумя датами.

главная таблица (parent table) Таблица, участвующая в связи "глав-ный-подчиненный", на одну строку (или запись) которой могут ссылатьсямногие строки (записи) подчиненной таблицы.

группа (group.) Совокупность записей, обычно являющаяся результатомSQL-запроса.

групповые значения (group values) Значения (или наборы значений), об-щие для всех записей группы. Используются в конструкции HAVING для фи-льтрации групп целиком.

групповые функции (group functions) Функции Oracle, оперирующие сгруппами записей — например, SUM, AVG, MIN, МАХ, и т.д.

декартово произведение (Cartesian product) Соединение без конструк-ции WHERE, в результате которого каждая строка одной таблицы комбиниру-ется с каждой строкой другой таблицы.

диаграмма "сущность-связь" (Entity Relationship Diagram, ERD) Диа-грамма, на которой показана структура реляционной базы данных. Таблицыпредставляются определенными стандартными символами ("сущностями").Сущности соединяются при помощи стандартных символов и линий("связей"), отражающих природу связей между сущностями (таблицами).

жестко закодированный (hard-coded) Явно указанный в тексте сценарияили программы.

запись (record) Данные, содержащиеся в строке таблицы.

запись (record) PL/SQL Структура данных PL/SQL, состоящая из отдель-ных полей базовых типов PL/SQL.

344 : Глоссарий

заполненный (populated) Содержащий допустимое значение. Например,заполненное поле или заполненный атрибут.

значение по умолчанию (default value) Значение, автоматически при-сваиваемое переменной или другой сущности базы данных в момент созда-ния, если в коде отсутствует явное присваивание. Сохраняется до тех пор,пока не будет выполнено явное присваивание, перезаписывающее значениепо умолчанию.

. ;

именованная нотация (named notation) Способ вызова процедуры илифункции, при котором фактические параметры сопоставляются с формальны-ми параметрами при помощи нотации формальное_имя => фактическое_имя.Это позволяет размещать формальные параметры со значениями по умолча-нию в любом месте списка формальных параметров, а не только в конце.

индекс цикла (loop index) Переменная, используемая в цикле FOR дляподсчета проходов. Она указывается между FOR- и IN-частями оператораFOR.

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

итеративная обработка (iterative processing) Обработка строк или запи-сей по одной. Циклическая обработка, при которой на каждом проходе обраба-тываются различные данные.

конкатенация (concatenation) Операция, в результате которой две частитекста соединяются в один текст.

константа (constant) Выражение с фиксированным значением (напри-мер, 6/3).

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

литерал (literal) Фиксированный текст в команде или программе, которыйне подлежит изменению и интерпретируется буквально, а не рассматриваетсякак имя переменной.

многострочный подзапрос (multirow subquery) Подзапрос, который мо-жет возвращать более одной строки или записи.

модели данных (data models) См. диаграмма "сущность-связь".

Глоссарий 345

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

набор результатов, активный набор (result set, active set) Наборстрок, выбранных оператором DML SQL (SELECT, INSERT, DELETE илиUPDATE).

неявный курсор (implicit cursor) Скрытый курсор, используемый для опе-раторов DML в PL/SQL.

обработчик исключения (exception handler) Выполняемые операторы всекции исключений блока PL/SQL, производящие некоторые действия привозникновении исключения. Например, они могут выводить сообщение обошибке или пытаться ликвидировать причины исключения.

обрезание (trimming) Удаление избыточных пробелов в начале или концестроки.

\объектные привилегии (object privileges) Привилегии на определенныйобъект базы данных — таблицу, последовательность и т.д.

ограничения (constraints) Условия, которым должны удовлетворять дан-ные, чтобы быть принятыми базой данных.

однострочный подзапрос (single-row subquery) Подзапрос, возвраща-ющий не более одной строки или записи.

операторный триггер (statement trigger) Триггер, срабатывающий толь-ко один раз для каждого триггерного события (триггерного оператора).

операторы (operators) Техническое название символов математическихопераций, таких, как знак плюса и знак минуса.

операторы соединения (join operators) Операторы, обеспечивающиекомбинирование результатов двух SQL-запросов (операторов SELECT) раз-личными способами. К ним относятся UNION, MINUS, INTERSECT и др.

пакет (package) PL/SQL Программная единица PL/SQL, которая можетсодержать процедуры, функции и другие программные единицы PL/SQL. Па-кет раскрывает только свои интерфейсы (спецификации), которые могут испо-льзоваться другими программными единицами.

переменная (variable) Заместитель части команды в сценарии. В програм-мах — заместитель фактического значения.

переменная подстановки (substitution variable) Переменная в SQL-сце-нарии, замещающая входные данные, которые должны вводиться пользователем.

346 Глоссарий

переменные пакета (package variables) Переменные, объявленные вспецификации пакета. Они инициализируются только один раз — при загрузкепакета в память, и разделяются между всеми сеансами одного и того же пользо-вателя. Для инициализации этих переменных может использоваться выполня-емая секция пакета. Будучи частью спецификации пакета, переменные пакетадоступны всем его пользователям.

плоский файл (flat file) Одна большая таблица, в которой информацияхранится без использования связей, повторяясь столько раз, сколько необхо-димо. Например, в плоской таблице sales_order каждая строка с информацией озаказе определенного товара будет содержать полное описание этого товара.

подстрока (substring) Часть строки.

подчиненная таблица (child table) Таблица в связи "главный-подчинен-ный", которая ссылается на строки главной таблицы.

позиционная нотация (positional notation) Способ вызова процедурыили функции, при котором фактические параметры сопоставляются с форма-льными по их расположению в списке параметров. Чтобы этот способ работал,формальные параметры со значениями по умолчанию должны быть последни-ми в списке.

поле (field) Пересечение строки и столбца таблицы, содержащее единицуинформации о чем-либо.

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

представление (view) Запрос, хранимый в базе данных под определеннымименем.

преобразование данных (data conversion) Операция преобразованияданных от одного типа к другому. Чаще всего выполняются преобразованиямежду текстом и датами, временем или числами.

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

привязанный, или динамический, тип (anchored type, dynamic type)Тип переменной PL/SQL, который автоматически приводится в соответствие стипом записи в курсоре или типом строки таблицы (объявляется путем указа-ния %ROWTYPE после имени курсора или таблицы), или типом столбца таб-лицы (объявляется как имя_столбца%ТУРЕ).

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

Глоссарий 347

приоритет операторов (operator precedence) Последовательность вы-полнения различных операций в выражении с более чем одним оператором. На-пример, в выражении 3 - 4 / 2 сначала будет выполнено деление, что дастпромежуточное значение 2, а затем вычитание, что даст окончательное значение 1.

процедура (procedure) PL/SQL Часть кода PL/SQL, состоящая из одногои более базовых блоков PL/SQL, которые выполняют строго определенныедействия. Процедура может иметь набор входных и выходных данных, называ-емых формальными параметрами, но не имеет возвращаемых значений.

псевдоним столбца (column alias) Имя-заменитель, присваиваемое столб-цу в SQL-командах. Например, в приведенном ниже коде "Sold By" являетсяпсевдонимом столбца salesperson:

. ' • - • . : •"•"', • ' ' • • . - с" '-.";,•• •• yf, •-•;:; ц • •SELECT product_name || ' was sold by ' |I salesperson "Sold By"

FROM plsql!01_purchase;

псевдоним таблицы (table alias) Сокращенное имя, которое можно при-своить таблице в SQL-операторе и затем использовать в пределах этого оператора.

путь (path) Имя дискового накопителя и каталога, где хранится файл.

разбор (parsing) Процесс разбиения строки на подстроки.

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

режимы передачи параметров (modes of parameters) Определяют, бу-дет ли фактический параметр только считываться, только записываться, или исчитываться, и записываться вызываемой функцией или процедурой. Указы-ваются в описании формального параметра с помощью ключевых слов IN(только чтение), OUT (только запись) и IN OUT (чтение-запись).

реляционная база данных (relational database) База данных, в которойинформация организована в виде связанных таблиц.

• • - . • .

роль (role) Набор привилегий. Роли упрощают предоставление одинаковыхнаборов привилегий многим пользователям.

связь (relationship) Ассоциация между двумя и более таблицами, установ-ленная на уровне столбцов. Например, значения в столбце product_id таблицыsales_order должны быть взяты из одноименного столбца таблицы product_cata-log. Для установления такой связи столбец product_id таблицы sales_order объ-является внешним ключом, ссылающимся на столбец product_id таблицыproduct_catalog.

348 Глоссарий

связь "главный-подчиненный" (parent-child relationship) Связь междудвумя таблицами, при которой на одну запись первой таблицы (главной) могутссылаться многие записи второй таблицы (подчиненной).

связь "один ко многим" (one-to-many relationship) Связь между табли-цами, при которой на одну запись в одной таблице могут ссылаться многиезаписи другой таблицы.

символьные функции (character functions) Функции Oracle, предназна-ченные для работы с текстовыми строками.

синоним (synonym) Альтернативное имя, присвоенное существующемуобъекту базы данных.

синтаксис (syntax) Правила записи команды или языковой конструкции.Только команды с правильным и полным синтаксисом могут быть успешновыполнены.

системная привилегия (system privilege) Привилегия на действия вовсей базе данных — скажем, на вставку в любую таблицу.

словарь данных (data dictionary) Набор таблиц и представлений, автома-тически создаваемых СУБД (в частности, Oracle) для хранения текущей ин-формации обо всех объектах базы данных.

составной индекс (composite index) Индекс по более чем одному столбцутаблицы.

спецификация (specification) Информация, уникально идентифицирую-щая и описывающая программную единицу PL/SQL.

спецификация процедуры (procedure specification) Спецификацияпроцедуры PL/SQL состоит из имени процедуры и списка ее формальныхпараметров.

спецификация пакета (package specification) Спецификация пакетаPL/SQL содержит имя пакета, а также объявления его переменных, функций,процедур, типов и курсоров.

спецификация функции (function specification) Спецификация функ-ции PL/SQL содержит имя функции, список ее формальных параметров и типвозвращаемого значения.

строка (row) Один ряд ячеек таблицы.

строка (string) Текст.

строчный триггер (row trigger) Триггер, выполняемый для каждой стро-ки, модифицированной в результате триггерного события.

Глоссарий 349

столбец (column) Набор данных одного типа, хранящихся в таблице (на-пример, все телефонные номера или все фамилии).

' ' ' ' . • - ' • •* '

сцепленный индекс (concatenated index) См. составной индекс.

таблица (table) Набор строк и столбцов, в которых хранится информацияоб объектах одного типа (например, о людях или товарах).

табличный триггер (table trigger) Триггер, определенный для таблицы.

тело пакета (package body) Фактический код пакета, содержащий пол-ные определения объектов, объявленных в его спецификации, а также опера-торы, инициализирующие переменные пакета.

тип данных (datatype) Тип хранимых данных — например, числовой(NUMBER), символьный (CHAR) и т.д.

триггер (trigger) Блок PL/SQL, автоматически выполняемый в ответ натриггерное событие.

триггер INSTEAD OF Триггер, определенный для представления и приме-няемый к его базовым таблицам, при условии, что представление удовлетворя-ет определенным критериям.

триггерное действие (trigger action) Выполняемая часть триггера. Фак-тическое действие, предпринимаемое при срабатывании триггера, когда онудовлетворяет ограничениям.

триггерное событие, или триггерный оператор (trigger event, triggerstatement) Событие (например, выполнение оператора DML), которое мо-жет использоваться для запуска триггера на выполнение.

триггерное ограничение (trigger restriction) Условие, которому должныудовлетворять старые и новые данные, чтобы триггер был выполнен. Напри-мер, новое значение больше старого.

уникальный индекс (unique index) Индекс, созданный Oracle для реали-зации ограничения уникальности.

условная обработка (conditional processing) Выполнение различныхгрупп операторов в зависимости от результата проверки некоторого условия.

фактические параметры (actual parameters) Реальные переменные иликонстанты, которые обычно копируются в формальные параметры функцииили процедуры при ее вызове. Существуют способы избежать копирования, нерассмотренные в этой книге.

формальные параметры (formal parameters) Список имен и типов пе-ременных, которые функция или процедура принимает от вызывающего кода.Формальные параметры можно рассматривать как локальные переменныефункции или процедуры.

350 Глоссарий

функция (function) Часть кода программы — встроенная или написаннаяпользователем, которая принимает один (входной) набор значений и возвра-щает другой (выходной) набор значений, вычисленных на основе входныхзначений.

функция (function) PL/SQL Часть кода PL/SQL, состоящая из одного и бо-лее базовых блоков PL/SQL, которые вычисляют единственное значение и воз-вращают его при вызове. Функция может принимать набор входных данных,называемых формальными параметрами.

ход выполнения (control flow, execution flow) Последовательность вы-полнения операторов программы. Эту последовательность можно изменять взависимости от условий. Конструкции, обеспечивающие управление ходомвыполнения, называются управляющими операторами (flow control statements).Примерами таких операторов в PL/SQL служат IF и LOOP.

шаблон (wildcard) Символ, представляющий произвольный текст.В SQL-командах в качестве шаблона используется знак %. В приведенномниже примере будут выбраны все товары, название которых начинается на"Chrome".

SELECT * FROM plsq!101_product

WHERE product_name LIKE 'Chrome%';

юлианский календарь (Julian dates) Система летосчисления, основан-ная на подсчете количества дней, прошедших с определенного начального дня.В Oracle начальным днем считается 1 января 4712 г. до н.э. Время суток выража-ется как дробная часть даты. Например, 54321.5 означает полдень 54 332-го дняс 1 января 4712 г. до н.э.

явный курсор (explicit cursor), или просто курсор Курсор, явно объяв-ленный программистом с использованием синтаксиса CURSOR ... IS. Кур-сор — это структура-посредник, используемая для организациивзаимодействия между PL/SQL и SQL. Явный курсор содержит набор строк,выбранных его оператором SELECT. Эти строки могут по очереди выбиратьсяиз курсора для обработки в коде PL/SQL.

PL/SQLИспользуйте SQL и PL/SQL в базе данных OracleВ книге объясняется, как использовать SQL для работы с базой данных икак автоматизировать сложные задачи с помощью PL/SQL. Вы будете учитьсяна конкретных примерах: каждая глава содержит практические упражнения,помогающие освоить представленный материал. Изложение ведется попринципу "от простого к сложному". Сначала рассматриваются основыбаз данных, включая такие фундаментальные понятия, как "таблица","строка", "запись", "столбец" и "поле", а затем объясняется, как сохранять,извлекать и модифицировать данные, управлять программой SQL*Plus,создавать SQL-функции и, наконец, писать программы на PL/SQL.Вы научитесь:

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

Писать SQL-команды для вставки, выборки, обновления иудаления данных

Выполнять сложные манипуляции с данными

Писать законченные функции и процедуры PL/SQL

Объявлять переменные с использованием привязанных типов

Создавать пакеты PL/SQL

Использовать триггеры для реализации сложных бизнес-правил иподдержания безопасности

Книга написана сертифицированным специалистом по Oracle и одобренакорпорацией Oracle. Она даст все сведения, необходимые для начала работыс SQL и PL/SQL.

Издательство "Лори'www.lory-press.ru

Oracle PressI T I O N


Recommended