101: Oracle PL/SQL, Как писать мощные и гибкие программы на PL/SQL
 007212606X, 5855821390 [PDF]

  • 0 0 0
  • Suka dengan makalah ini dan mengunduhnya? Anda bisa menerbitkan file PDF Anda sendiri secara online secara gratis dalam beberapa menit saja! Sign Up

101: Oracle PL/SQL, Как писать мощные и гибкие программы на PL/SQL 007212606X, 5855821390 [PDF]

В книге объясняется, как использовать SQL для работы с базой данных и как автоматизировать сложные задачи с помощью PL/S

125 33 45 MB

Russian Pages 369 Year 2001

Report DMCA / Copyright

DOWNLOAD FILE


File loading please wait...
Citation preview

Oracle Press



oirv/



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



f



ORACLE



Oracle Press TM



Oracle PL/SQL 101



Christopher Allen



Osborne/McGraw-Hill П : ' ..-• , . - . , -



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



•'.- , • ' • > ' . :



101: Oracle PL/SQL



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



: .



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



Oracle PL/SQL 101 Christopher Allen Copyright © 2001 by The McGraw-Hill Companies, Inc. All rights reserved. Osborne/McGraw-Hill 2600 Tenth Street Berkeley, California 94710 U.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-62 WWW.LORY-PRESS.RU Отпечатано в типографии ООО "Типография ИПО профсоюзов Профиздат" 109044, Москва, ул. Крутицкий вал д. 18



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



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



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



Содержание ' Благодарности Об авторе Предисловие



vi vii vii



•чмЬмм)



;



Часть I Основы баз данных —— Глава 1 Введение в базы данных Что такое база данных? Таблицы Строки/записи Столбцы/поля Чем база данных отличается от электронной таблицы? Хранение многих строк Одновременное обслуживание многих пользователей Безопасность Реляционные свойства. Ограничения, гарантирующие качество данных Вопросы для повторения Практическое задание



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



3 4 4 5 5 5 7 7 7 8 9 9 10



10 10 11 11 11 12 12 13 13



Содержание Управление денными , , i, , Выборка Данньйс Управление транзакциями Итоги Вопросы Ответы на вопросы •' >,







.• • . •



. >:,, ! : -,.



' • • •"-:•' '.«•" -:' 1 ':/'.]



~j ^^:.с.£ I, •;



I



.



.



Глава 2 Сохранение и выборка данных: основы Первые шаги Создание таблицы Вставка записей Выбор записей Удаление таблицы Создание таблиц Именование таблиц и столбцов Правила Рекомендации



;



;



13 14 14 14 16 17



'



19 20 20 21 21 23 25 25



р., .•;• .,';,' '-



.' • : .2 94 95



xii



Содержание Очистка экрана SQL*Plus Настройка среды SQL*Plus Настройка с использованием меню SQL*Plus Настройка с использованием команд Сохранение настроек среды Форматирование выходных данных SQL*Plus Форматирование чисел



97 97 98 100 100 101 102



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



102 102 103 103



Форматирование текста Форматирование заголовков столбцов Буферизация выходных данных на диске Файлы сценариев SQL Создание файла сценария Запуск сценария Использование переменных в файлах сценариев



103 105 107 108 108 109 109



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



110 111



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



111 112 113



' -



Часть SQL для профессионалов Глава 5 Встроенные функции SQL Часто используемые однострочные функции Системные переменные SYSDATE USER USERENV



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



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



. 117 119 119 119 120 121



121 122 123



12 4



;



xiii UPPER, LOWER и INITCAP LENGTH SUBSTR INSTR LTRIMnRTRIM



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



124 126 127 129 133



136 136 137 139 141



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



142 142



Прочие функции



151



TO_DATE



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



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



Группирование данных с помощью конструкции GROUP BY Включение и исключение групп с помощью конструкции HAVING Итоги Вопросы иг Ответы на вопросы ''"'.•-'•'•• ••'



147



152 156 156 158



160 161 162 163 163 163



164 166 166 ;~ 167 168



Глава 6 Индексы и ограничения Индексы



171 173



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



174 176 177 178



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



178 179



xiv



,_.,..



Содержание



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



181 182 182



NOT NULL UNIQUE CHECK



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



190 192



193 194 194 196 198



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



202 207 208 209



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



211 211 211 212 214 215 216 217 218



,



UNION ALL INTERSECT MINUS



**••,'* ,-"'•' .'



'-



182 183 186



•"'



' "



'"•



-,''



*t



••'".



"••



:



• ''••'



' " . ' . "



)



*



; '



''-



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



199



210 211 211



' " ' ' . " • • • ,



Глава 7 Другие полезные средства Oracle Перенос данных между таблицами Перенос данных с помощью INSERT Создание новой таблицы на основе уже существующей Переименование таблиц



221 225 226 228 229



XV



Изменение структуры таблицы Добавление столбцов Изменение типа данных столбца Изменение null-опций Представления Создание представления Удаление представлений Изменение определения представления Анализ первых JV записей Другие объекты базы данных



, ,



v



;



,



j



Последовательности



236



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



Часть III Создание программ на PL/SQL Глава 8 Введение в PL/SQL Что такое PL/SQL? Общие сведения о PL/SQL SQL, PL/SQL и SQL*Plus: кто есть кто Хранимые процедуры, функции и триггеры Хранимые процедуры , Хранимые функции Триггеры Хранимые процедуры в сравнении с SQL-сценариями



229 229 230 231 232 233 235 235 235 236 236 237 239 239 241 242 242



242 242 244 245 246



.. 251 252 255, 256 257 257 258 259 259



xvi



Содержание Структура блока PL/SQL Секция заголовка Секция объявлений Выполняемая секция Секция исключений Создание простой PL/SQL-процедуры Вызов процедур и функций Переменные и константы PL/SQL Объявление переменных PL/SQL Объявление констант PL/SQL Присваивание значений переменным Использование переменных Управляющие структуры в PL/SQL Оператор IF Циклы LOOP Цикл WHILE Цикл FOR



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



Вложенные циклы и пример курсора Обработка ошибок Исключения Системные исключения Исключения, определяемые программистом Итоги Вопросы Ответы на вопросы



Глава 9 Другие средства PL/SQL Соглашения о кодировании Подробнее о взаимодействии PL/SQL и сервера Oracle Динамическое объявление типов переменных и записи PL/SQL



259 260 261 261 262 263 264 266 266 267 267 269 269 270 272 272 274 275



275 276 277 278 278 279



280 282 282 284 286 288 289 290



293 298 299 300



XVII



DML в PL/SQL, или неявные курсоры Сравнение явных и неявных курсоров Операции с временем Измерение времени в программе Использование команды TIMING для счета реального времени Пакеты PL/SQL Триггеры Типы триггеров Пример триггера Модификация триггеров Тонкости, касающиеся триггеров ODBC • • - • ' Создание ODBC-соединения Применение ODBC-соединений Просмотр и редактирование данных Access Импорт данных Oracle в Excel



Итоги Вопросы Ответы на вопросы Глоссарий



305 310 311 311



- '



312 315 321 323 324 327 327 328 328 333 333 335



338 338 339 341



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







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



4



Глава!



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



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



Таблицы Базы данных всегда разрабатываются для хранения информации определенного типа. Например, в случае телефонного справочника это информация о людях (на белых страницах) и о коммерческих организациях (на желтых страницах). Для хранения такой информации база данных в общем случае должна иметь две таблицы: одну для людей, а другую для организаций. Каждая из них будет во многом похожа на электронную таблицу (spreadsheet), имея по одному столбцу для каждого типа хранимой информации (имени, адреса, номера и т.д.) и по одной строке для каждого физического лица или организации. Например, простая таблица с информацией о служащих могла бы выглядеть так: EMPLOYEE_ID FIRST_NAME LAST_NAME SALARY HIRE_DATE 1024 2048 3072 4096



,



Scott Campbell 63000 Linda Hammond 68000 Dave Anthony 69000 Tiff Berlin 66000



17-FEB-98 15-JAN-99 11-APR-OO 24-DEC-01



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



5



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



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



Столбцы/поля Каждая строка содержит несколько единиц информации. В примере со служащими к ним относятся личный идентификатор, имя и т. д. Эти информационные единицы хранятся в столбцах (columns) таблицы. Точка пересечения строки и столбца — например, имя определенного служащего — называется полем (field). Поле содержит одну единицу информации о чем-либо (например, телефонный номер одного человека). Рассмотрим конкретный пример. Представьте, что вам нужно поместить информацию о пяти своих друзьях на стандартные индексные карточки размером 3x5. Для каждого из друзей вы заводите отдельную карточку, поэтому всего их получится пять штук. Тем самым вы создаете небольшую базу данных. Это физическая база данных, а не компьютерная, но она все равно остается базой данных, и к ней применима концепция таблиц, записей и полей. На рис. 1.1 показано, чему соответствуют эти термины в случае индексных карточек. Теперь предположим, что вы поместили информацию о тех же пяти друзьях в электронную таблицу. На рис. 1.2 показано, как будут применяться только что изученные вами термины в этой ситуации.



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



6



Глава 1



Записи



Таблица



Свен Марк Джеймс Вирджиния Майкл



Шрамм Кинг. Бридструп Хэвлин



(555)234-5678



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



Поля



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



Комментарии Любит мотоциклы, фотографию, (555)234-5678 гпгшеЬпйпфгпа!! net 'игру на гитаре и больших собак Неутомимая пур шествекница и .3 Вирд :иния Хэвл* . (555)3 5-6789 *avel!rtg>r iai1.net 'просто хороший человек 4 Джей 1C Брид труп (555) 4i 6-7890 jbndslrupfc mail. ret Прозвище: Mr £ mooove _J| Если бы у меня iUM 5 'Марк Кинг '(555)51 7-8901 :mking@>m ii пйЕ незамужняя свс pa.v \s] : Перед тем. как j гтеть на J" :| Восточное побе| ежье. нужно 6 Свен Шрам и (555) 6; 8-9012 sschltmm Smaii net убедиться, что о i HE работает ^И 2



Строки



Майкл



Хабнер



н"< > и' ^Лист1/ПиС' г'/гьстэУ



' И .



,•



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 administrator) без знания 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, and Consistency of Relations Stored in Large Data Banks. Там описывался подход к структурированию баз данных, основанный на использовании связанных таблиц, который значительно отличался от принятого в то время подхода с плоскими файлами. Это сообщение имело пометку "для ограниченного распространения" и поэтому не получило широкой известности. Кодд переработал свои концепции и в 1970 г. опубликовал их в статье под названием A Relational Model of Data for Large Shared Data Banks в журнале ACM (Association of Computer 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/IEC 9051-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. Эта категория команд называется языком манипулирования данными (Data Manipulation 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, ROLLBACK nSAVEPOINT



Управление транзакциями



INSERT, UPDATE и DELETE



Ответы на вопросы 1. В, С, D, F Телефонный справочник; корешки чековых книжек; Web-сайты для проведения аукционов в реальном времени, заказа товаров и поиска; киноафиши. Объяснение По существу, база данных — это список, который может быть представлен в заданной последовательности и отфильтрован для показа только выбранных записей. Сообщения на первых полосах газет, как и объявления о продаже, не подпадают под это описание. 2. С



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



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



Описание Один ряд ячеек таблицы



18



Глава 1 Запись



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



Столбец



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



Поле



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



Таблица



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



База данных



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



Ограничение



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



4. А, С, D, Е, F Возможность хранения миллиардов строк; возможность одновременно обслуживать тысячи пользователей; обеспечение безопасности на уровне объектов; возможность связывания многих таблиц; возможность определять ограничения, которым должны удовлетворять данные, вводимые в базу данных. Объяснение Наличие любой из возможностей, за исключением В, служит веским основанием для использования базы данных в качестве хранилища больших объемов информации. Большие базы данных могут функционировать не только на PC — например, СУБД Oracle доступна для компьютеров с операционными системами Unix, Linux, Windows NT и рядом других. Доступность только на 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 Si for 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 3



first_nane CHflR(15), 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.



Глава 2



22



тшс



A. Oiacle SQL'Plus File ЕЛ Search Options Help SQL> CREATE TABLE plsq!101 test_1 ( 2 first_name CHAR(15), 3 last_name CHAR(20)



t



5 ;



>



Table created.



'



i • -.'•.



'.



SQL> 1 SQL> INSERT INTO plsql1B1_test_1 UALUES ('Jane , '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



Smith Allen



SQL> |



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



23



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



Удаление таблицы Чтобы завершить первое погружение в мир баз данных, удалим созданную таблицу. Примечание Удаление таблицы — это серьезный шаг! Он необратим! Используйте эту команду только при абсолютной уверенности в том, что записи таблицы больше не нужны. Для удаления таблицы введите следующую команду: DROP TABLE plsql!01_test_l;



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



*. Oracle SQL-Plus File Edit Search Options Help SQL> CREflTE TABLE plsqllB1_test_1 ( first_nane CHAR(15), 3 last name CHfiR(2B) it 5



Table created. SQL> SQL> INSERT INTO plsqllB1_test_1 UALUES ('Jane1, 'Smith1); 1 row created.



,.,!••.



.-,



,



-



.



SQL> INSERT INTO plsqll81_test_1 UALUES ('Christopher1, 'Allen1); 1 row created. SQL> SQL> SELECT * FROM plsqll01_test_1; FIRST NOME



LAST NAME



Jane Christopher



Smith flllen



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 3



»•;•: 5 ;



first папе CHflR(i5),



list MM



лу..



CHAR(20)



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 Christopher



Snith 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), вы сможете переходить от таблицы к таблице, говоря себе примерно следующее: "PURCHASE ORDER ссылается на PRODUCT...". • Не включайте слова TABLE или DATA в имя таблицы. Опытные пользователи понимают, что объектом базы данных, хранящим информацию, является таблица, а таблицы содержат данные. Не нужно напоминать им об этом.



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



27



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



ACCOUNT



ACTIVATE



ADD



ADMIN



AFTER



АИ



ALL_ROWS



ALLOCATE



ALTER



ANALYZE



AND



ANY



ARCHIVE



ARCHIVELOG



ARRAY



AS



ASC



AT



AUDIT



AUTHENTICATED



AUTHORIZATION



AUTOEXTEND



AUTOMATIC



BACKUP



BECOME



BEFORE



BEGIN



BETWEEN



BFILE



BITMAP



BLOB



BLOCK



BODY



BY



CACHE



CACHE INSTANCES



CANCEL



CASCADE



CAST



CFILE



CHAINED



CHANGE



CHAR



CHAR_CS



CHARACTER



CHECKPOINT



CHECK



CHOOSE



CHUNK



CLEAR



CLOB



CLONE



CLOSED, CACHED OPEN_ CURSORS



CLUSTER



COALESCE



COLUMN



COLUMNS



CLOSE '•• COMMENT



COMMIT



COMMITTED



COMPATIBILITY



COMPILE



COMPOSITE LIMIT



COMPRESS



COMPUTE



CONNECT



CONNECT.TIME



CONSTRAINTS CONSTRAINT



CONTENTS



CONTINUE



CONTROLFILE



CONVERT



COST



COUNT



CPU_PER_ CALL



CPU_PER SESSION



CREATE



CURRENT



CURRENT SCHEMA



CURRENT, USER



CURSOR



CYCLE



DANGLING



DATABASE



DATAFILE



DATAFILES



DATAOBJNO



DATE



DBA



DEALLOCATE



DEBUG



DEC



DECIMAL



DECLARE



DEFAULT



DEFERRABLE



DEFERRED



DEGREE



DELETE



DEREF



DESC



DIRECTORY



DISCONNECT



DISABLE



DISMOUNT



DISTINCT



DISTRIBUTED



DML



DOUBLE



DROP •



DUMP



EACH



ELSE



ENABLE



END



ENFORCE



ENTRY



ESCAPE



ESTIMATE



EVENTS



EXCEPTIONS



EXCHANGE



'



,



COMPLETE



Глава 2



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



EXCLUSIVE



EXECUTE



EXEMPT



EXISTS



EXPIRE



EXPLAIN



EXTENT



EXTENTS



EXTERNALLY



FAILED LOGIN ATTEMPTS



FALSE



FAST



FILE



FIRST.ROWS



FLAGGER



FLOAT



FLUSH



FOR



FORCE



FOREIGN



FREEUST



FREEUSTS



FROM



FULL -.n.



FUNCTION



GLOBAL



GLOBAL.NAME



GLOBALLY



GRANT



GROUP



GROUPS



HASH



HASHKEYS



HAVING



HEADER



HEAP



IDENTIFIED



IDLEJIME



IF



IMMEDIATE



IN



INCLUDING



INCREMENT



IND_ PARTITION



INDEX



INDEXED



INDEXES



INDICATOR



INITIAL



INITIALLY



INITRANS



INSERT



INSTANCE



INSTANCES



INSTEAD



INT



INTEGER



INTERMEDIATE



INTERSECT



INTO



IS



ISOLATION



ISOLATION LEVEL



KEEP



KEY



KILL



LAYER



LESS



LEVEL



LIBRARY



LIKE



LIMIT



LINK



UST



LOB



LOCAL



LOCK



LOG ov



LOGFILE



LOGGING



LOGICAL READS_PER_



LOGICAL READS PER CALL



LONG



-\v.?- i



-



MANAGE



MASTER



MAX



MAXARCHLOGS



MAXDATARLES



MAXEXTENTS



MAXINSTANCES



MAXLOGRLES



MAXLOGHISTORY



MAXLOGMEMBERS



MAXSIZE



MAXTRANS



MAXVAUJE



MEMBER



MIN



MINEXTENTS



MINIMUM



MINUS



MINVALUE



MODE



MODIFY



MOUNT



MOVE



MTS DISPATCHERS



MULTISET,



NATIONAL



NCHAR



NCHAR.CS



NCLOB



NEEDED



NESTED



NETWORK



NEW



NEXT



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



NLS_



Сохранение и выборка данных: основы Таблица 2.1 (продолжение)



29 •' '• : 404 '" "'







NLS ISO CURRENCY



NLS LANGUAGE



NLS NUMERIC CHARACTERS



NLS.SORT



NLS TERRITORY



NOARCHIVELOG



NOAUDIT



NOCACHE



NOCOMPRESS



NOCYCLE



NOFORCE



NOLOGGING



NONE



NOMAXVALUE



NOMINVALUE



NOORDER



NOOVERIDE



NOPARALLEL



NORESETLOGS



NOREVERSE



NORMAL



NOS SPECIAL CHARS



NOSORT



NOT



NOTHING



NOWAIT



NULL



NUMBER



NUMERIC



NVARCHAR2



OBJECT



OBJNO



OBJNO_REUSE



OF



OFF



OFFLINE «



OID



OIDINDEX



OLD



ON



ONLINE



ONLY



OPCODE



OPEN



OPTIMAL



OPTIMIZER GOAL



OPTION



OR



ORDER



OVERFLOW



ORGANIZATION



OWN



PARTITION



PASSWORD



PASSWORD UFEJIME



PASSWORD LQCKJIME



PASSWORD REUSEJWX



PASSWORD VERIFY FUNCTION



PASSWORD GRACEJIME



PCTINCREASE



PCTTHRESHOLD



PERCENT



PCTVERSION



PERMANENT



PLAN



PLSQL DEBUG



POST TRANSACTION



PRECISION



PRESERVE



PRIMARY



PRIOR



PRIVATE



PRIVATESGA



PRIVILEGE



PRIVILEGES



PROCEDURE



PROFILE



PUBLIC



PURGE ~



QUEUE



QUOTA



RANGE



RAW



RBA



READ



REAL



REBUILD



RECOVER



RECOVERABLE



RECOVERY



REF



REFRESH



REFERENCES



REFERENCING



RENAME



REPLACE



RESET



RESIZE



RESETLOGS



RESOURCE



RESTRICTED



RETURN



RETURNING



REUSE



REVERSE



REVOKE



ROLE



ROLES



ROLLBACK



ROW



ROWID



ROWLABa



ROWNUM



ROWS



RULE







PCTFREE



.



;



' PACKAGE



8



;



PARALLEL .. :,-...' :,••;•.. PASSWORD REUSEJIME PCTUSED '-r'



^



Глава 2



30



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



SAVEPOINT



SCAN INSTANCES



SCHEMA



SCN



SCOPE



SD.ALL



SDJNHIBIT



SD_SHOW



SEG_BLOCK



SEG_FILE



SEGMENT



SELECT



SEQUENCE



SERIALIZABLE



SESSION



SESSION CACHED CURSORS



SESSIONS PER USER SIZE I SPECIFICATION



'



5



SET



SHARE



SHARED



SHARED_POOL



SHRINK



SKIM UNUSABLE .INDEXES



SMALLINT



SNAPSHOT



SOME



SORT



SPLIT



SQLJRACE



SQLCODE



SQLERROR



STANDBY



• START



STOP



STATISTICS



STORAGE



STRUCTURE



STORE



•• SUM



STATEMENT ID SUCCESSFUL



SWITCH



SYNONYM



SYSDATE



SYSDBA



SYSOPER



SYSTEM



TABLE



TABLES



TABLESPACE



TABNO



TEMPORARY



THAN



THE



THEN



TABLESPACE NO ' " THREAD



TIME



TIMESTAMP



TO



TOPLEVEL



TRACE



TRACING



TRANSACTION



TRANSITIONAL



TRIGGER



TRIGGERS



TRUE



TRUNCATE



TX



TYPE



UBA



UID



UNARCHIVED



UNDER



UNDO



UNION



UNIQUE



UNLIMITED



UNLOCK



UNRECOVERABLE



UNTIL



UNUSABLE



UNUSED



UPDATABLE



UPDATE



USAGE



USE



USER



USING



VALIDATE



VALIDATION



VALUE



VALUES



VARCHAR



VARCHAR2



VARRAY



VARYING



VIEW



WHEN



WHENEVER



WHERE



WITH



WITHOUT



WORK



WRITE



XID



.



i



i







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



/



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



Глава 2



34



Примечание Возможно, вы заметили, что текст в команде 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 для его сохранения в формате Text Only, то результатом будет файл в кодировке ASCII. (Это справедливо только в том случае, если документ содержит английский текст. — Прим, пер.) ASCII-файлы могут быть открыты в любом текстовом процессоре или редакторе.



Как Oracle сохраняет числа



Для определения столбцов, в которых будут храниться только числа, используется тип данных NUMBER.-При определении столбца типа NUMBER также указывается, сколько цифр должен хранить столбец. Эта спецификация может состоять из двух частей: количества цифр до десятичной точки и количества цифр после десятичной точки. Предположим, что вам нужно создать таблицу для хранения цен на товары. Все цены не превышают ста долларов. Чтобы увидеть, как создается и используется такая таблица, введите следующие команды: CREATE TABLE plsq!101_product ( product_name VARCHAR2(25), product_price NUMBER(4,2)



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



INTO INTO INTO INTO



plsql!01_product plsql!01_product plsql!01_product plsql!01_product



VALUES VALUES VALUES VALUES



('Product ('Product ('Product ('Product



35 Name Name Name Name



1', 2', 3', 4',



1); 2.5); 50.75); 99.99);



SELECT * FROM plsql!01_product; DROP TABLE plsql!01_product; Когда вы закончите, экран должен выглядеть так, как показано на рис. 2.7.



Ж Oiacle SQL'Plus File £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 1 Product; Name 2



Product Name 3 Product Name Ц



1 2.5 50.75 99.99



SQL> SQL> DROP TABLE plsqll81_product;



Table dropped. SQL>



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



36



Глава 2



Как вы могли заметить, объявление типа данных NUMBER имеет следующий синтаксис: 'Ы\]МВЕЩобщее_число_цифр,



число_цифр_после_десяттной_точки)



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



Как Oracle сохраняет даты



Даты представляют для компьютеров интересную проблему. Рассмотрим, например, такой список дат: January 15, 2002 February 15, 2002 March 15,2002 Этот список можно хранить в текстовом столбце, но даты не будут правильно отсортированы, поскольку текстовые столбцы сортируются слева направо, а буква "F", с которой начинается слово February, стоит перед "J" (January) и "М" (March). Чтобы обойти эту проблему, месяцы можно представлять числами, а не названиями. В результате мы получим следующий список: 01-15-2002 02-15-2002 03-15-2002 Теперь месяцы будут отсортированы правильно. Если бы дни каждого месяца отличались, то такой подход все равно бы сработал, поскольку символы, обозначающие день месяца, стоят после символов месяца. Но если отличаются годы, возникает проблема. Возьмем такой вариант списка: •;v 01-15-2010 02-15-2005 03-15-2000 Если эти даты сохранить в текстовом столбце, а потом отсортировать, то они будут расположены именно в таком порядке, поскольку текстовые столбцы, как уже упоминалось, сортируются слева направо. Вторая запись начинается с символов "02", поэтому она будет помещена после записи, начинающейся с "01", независимо оттого, какие символы идут дальше. Эту проблему можно решить, поместив год на первое место. При таком подходе список мог бы выглядеть следующим образом: 2010-01-15 2005-02-15 2000-03-15 С точки зрения сортировки здесь все хорошо. При считывании символов слева направо отсортированная версия списка примет вид:



Сохранение и выборка данных: основы 2000-03-15 2005-02-15 2010-01-15



37



;/



Это изящное решение, если все, что вам нужно делать с датами, — это сортировать их в хронологическом порядке. Однако во многих ситуациях даты требуется не только сортировать. В бухгалтерии хотят знать, какие дебиторские задолженности должны быть погашены в течение следующих 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 ' )



Глава 2



38 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 Help SQL> CREflTE TftBLE plsql101_purchase ( 2 product_name UARCHAR2(25),



3 4 5 6 ;



product_price NUMBtR(4,2), purchase_date ОПТЕ )



Table created. SQL> SQL> INSERT INTO plsqll01_purchase UflLUES 2 ('Product Name 1', 1, 'S-NOU-ee1);



1 row created. SQL> INSERT INTO plsqll81_purchase UftLUES 2 ('Product Name 2', 2.5, '29-JUN-B1' );



1 row created. SQL> INSERT INTO plsqllB1_purchase UflLUES 2 ('Product Name 3', SB.75, 'IB-DEC-eZ1); 1 row created. SQL> INSERT INTO plsql1B1_purcHase UflLUES 2 ('Product Name V, 99.99, '31-1ШС-вЗ'); 1 row created. SQL> SQL> SELECT * FROM plsqllB1_purcnase; PRODUCT НЙМЕ



Product Product Product Product



Name Name Name Name



PRODUCT PRICE PURCHASE



1 2 3 4



1 2.5 58.75 99.99



05-NOU-00 29-JUN-B1 18-DEC-B2 31-flUG-B3



SQL> |



Рис. 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?



PRODUCT_NA№ PRODUCT_PRICE PURCHflSE_DOTE



Type UflRCHflR2(25) NUMBER(4,2) 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, product_price NUMBER(4,2) NOT NULL,



purchase_date DATE



r';> .;.,• .,



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



3 i» 5



6 ;



product_nane



UftRCHflR2(25) NOT NULL,



product_price NUHBER(I»,2) purchase_date DATE )



NOT NULL,



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 Product Name 1



PRODUCTJMUCE PURCHftSE_ 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 2 (NULL, 2.5, '29-JUN-ei'); INSERT INTO plsqll01_purchase UALUES



^



jj -J



w



ERROR at line 1: ORO-01itOO: cannot insert NULL into' (• l PLSQLiai"."PLsqL101_PURCHflSE". ll PRODUCT_NAME") SQL> INSERT INTO plsqll01_purchase UALUES 2 ('Product Name 3 1 , null, 'lO-DEC-BZ 1 ); INSERT INTO plsqll01 purchase UALUES * ERROR at line 1: OHfl-01!»00: cannot insert NULL into ( 11 PLSQL181' 1 ."PLSQL101_PURCHASE". >1 PRODUCTJ > RICE")



SQL> INSERT INTO plsqll01 purchase UALUES 2 (NULL, NULL, 'ai-flUG-BS 1 ); 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 Name 1



.



-







'



.



.



/



,



'



PRODUCT_PRICE PURCHASE_ 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 Help SQL> QL> INSERT INSERT INTO INTO plsq!1B1_purchase plsqll (product_nane, 2 UflLUES С Product Name 2 ' , 2.5);



product_prlce)



1 row created. 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 Name 1 Product Name 2



Product Name 3



Product Name i»



PRODUCT_PRICE PURCHASE.



1 2.5



58.75



99.99



SQL>



JJJ



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



jj



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



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



ИИ 13!



£!е ЕА Search Qptiora Help .'••• SQL> INSERT INTO plsq!101_purchase UflLUES



2



('Fifth Product1s Name', 25, '05-MftV-03');



ERROR: ORfl-61756: quoted string not properly terminated



SQL>



Рис. 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 Help SQL> SET SCftN OFF SQL> SQL> INSERT INTO plsqliai_purchase UftLUES 1 2 ('Fifth Product 's Name', 25, 'OS-MAY-OS'); 1 row created. SQL> SQL> SET SCAN ON SQL> SQL> SELECT * FROM plsq!1B1_purchase; PRODUCT NAME



PRODUCT PRICE PURCHASE



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



1 2.5 58.75 99.99 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 Help SQL> SELECT product_narae FROM plsqll01_purchase; PRODUCT NftME Product Name 1 Product Name 2 Product Name 3 Product Nane 4 Fifth 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_name FROM plsql!01_purchase; Результат должен быть таким, какнарис. 2.17. Чтобы лучше освоить эту технику, выберите записи из таблицы PLSQL101_PURCHASE несколько раз, по-разному располагая столбцы.



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



Глава 2



48



fte £ SELECT product_nane, product_price * 1.15 FROM plsql101_purchase; PRODUCT NflME



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



PRODUCT PRICE*1.15



1.15 2.875



58.3625



114.9885



28.75



SQL>



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



Математические операторы



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



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







'



.



.



.



-



.



-



-



.



.



.



- .



(



.



.



-



,







,



-



.



-



:



.



.



-



,



49 .



,



,



...



-.,



• f SELECT product name, product_price + sales_tax FROM plsql181_purchase; _±j



PRODUCT НЙМЕ Product Product Product Product



Name Name Name Name



PRODUCT PRICE+SflLES TUX



1.88 2.71 51». 94



1 2 3 4



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



Name Name Name Name



180-PRODUCT PRICE



99 97.5



1 2 3 4



49.25



.81



SQL> SELECT productjnane, sales_tax / product_price FROM plsql181_purchase; PRODUCT NflME Product Product Product Product



Name Name Name Name



SftLES ТИХ/PRODUCT PRICE 1 2 3 4



.88 .084 .082561576 .882588251



SQL> |



Рис. 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 b y ' I | salesperson FROM plsql!01_purchase;



Результаты должны быть такими, как на рис. 2.20. Фиксированный текст, включенный в команду, называется литералом (literal). Это означает, что он будет воспроизводиться символ за символом, а не интерпретироваться как, имя таблицы, столбца или другого объекта. ЯНЕ31



* Oiacle SQL-Plus File Edit Search Options Help SQL> SELECT product_name || ' was sold by ' | | salesperson 2 FROM plsq!101 ^purchase;



• ^ « • ^ • ^ ^ ^ • • • « • • ^ ^ ^ • • • • • • ^ • ^ • • • • • • • ^ ^ ^ • • • • • « • ^ ^ • • • • • « • ^ • • • • • • ^ • • • • • • • • ^ • • • • • • • • ^ • • • • • • • Р ^ ^ Н В В Н И М Н ^ Н В



d



1



PRODUCT _NftME | | 'UASSOLDBV | | SALESPERSON Product Product Product Product



Name Name Name Name



1 2 3 4



was was uas Mas



sold sold sold sold



by by by by



ftB CD EF CH



.



;



.



-



'



-



.



SQL>



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



I



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 Help SQL> SELECT product_nane 11 • was sold by ' || salesperson "Sold By1' 2 FROM plsq!1B1_purchase;



НщЦЗ



Sold By Product Product Product Product



Mane Name Name Name



1 2 3 4



was sold by ftB was sold by CD was sold by EF 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 Standard Code for Information Interchange.) Стандарт ASCII предназначен для приведения информации к "общему знаменателю", чтобы обеспечить ее перенос между компьютерами. Для числовых столбцов в отличие от текстовых Oracle предлагает всего один базовый тип данных с названием NUMBER. При создании числового столбца вы просто указываете максимальное количество цифр, которое он может содержать, вместе с требуемым количеством цифр после запятой. Наибольшее число, которое можно хранить, составляет 999 999 999 999 999 999 999 999 999 999 999 999 990 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 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 | | " " II



•;,'.;



last_name "Full Name"



FROM



plsql!01_person;



A. 1



-.B.2;-;;, с. з D. 4 • : E. 5' .. i . .-: .,, •• :•



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



;,j4^ ,••: ;• . -: j



Сохранение и выборка данных: основы 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. Пробел — это текст, а текст должен заключаться в одиночные, а не двойные кавычки. Единственным исключением



Глава 2



58



являются псевдонимы столбцов, которые требуется заключать в двойные кавычки при наличии в них пробелов или букв разных регистров. В этом случае двойные кавычки помогают 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_product WHERE quantity_on_hand = 1; Результат показан на рис. 3.1. Теперь, чтобы лучше освоить эту технику, выберите записи, в которых цена равна 25. Следующий шаг будет состоять в выборе записей со значениями, которые больше или меньше определенной величины. Например, чтобы найти товары, запасы которых требуют пополнения, можно использовать следующую команду: SELECT * FROM plsql'101_ptoduct WHERE quantity_on_hand < 500; Введите эту команду и сравните ее результаты с показанными на рис. 3^2. Последняя команда исключала записи, для которых количество товара на складе равно 500. Если вам нужно выбрать записи со значениями, меньшими или равными определенной величине, поставьте знак равенства после знака "меньше, чем". Чтобы увидеть, к чему приводит такое изменение, введите следующую пару команд: SELECT * FROM plsql!01_product WHERE quantity_on_hand < 1000; SELECT * FROM plsql!01_product WHERE quantity_on_hand SELECT « F R O M plsqll Byproduct 2 WHERE quantity_on_hand - 1; PRODUCT НИНЕ Snail Widget



PRODUCT_PRICE QUflNTITV_OH_HAND LflST_STOC



99



1 15-JAH-03



SQL>



-iLJ



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



Глава 3



62



•TTTnTxl



• & Oracle SQL-Plus File Edit Search Options Help SQL> SELECT * FROM plsqll 01 product 2 WHERE quantity_on_hand < 500; PRODUCT_NflME



d



PBODUCT_PRICE QUflNTITV_ON HfiND LflST STOC



Snail Widget Chrome Phoobar



99 58



1 15-JflH-83 100 15-JflN-l03







SIJL>



-



'



-







.



' :



•:•'••







: ;



.



.



.



;



.



: • - . . ,



.



-



'•-•.'



.



::•'..



'







«I I



4



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



тшт



Ж Oracle SQL'Plus File £dit Search Options Help SQL> SELECT » FROM plsql181_product 2 WHERE quantitp_on_hand < 1000; PRODUCT NAME Small Widget Chrome Phoobar



PRODUCT_PRICE qUHNTITV_ON_HflND LflST_STOC 99 SO



1 15-JflN-03 100 15-JflN-03



SQL> SQL> SELECT * FROM plsqll01_product 2 WHERE quantity_on_hand



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



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



63



SELECT * FROM plsql!01_product WHERE quantity_on_hand > 1000; SELECT * FROM plsql!01_product WHERE quantity on hand >= 1000;



'



,.'



i



Выбор записей по диапазону значений



Следующий шаг состоит в выборе записей, содержащих значения из определенного диапазона. Чтобы определить диапазон, нужно просто задать его нижний и верхний пределы. Для этого вам придется освоить новую технику, а именно: научиться указывать, что для прохождения записи через фильтр должны быть одновременно выполнены два разных условия. На самом деле тут нет ничего сложного: достаточно соединить два условия словом "AND". Следующий код показывает, как это делается: SELECT * FROM plsq,1101_product WHERE product_price >= 50 AND product_price SELECT product_name 2 FROM plsqll81_purchase 3 ORDER BV product_nane;



:



:



Л



'^



PRODUCTJfflME Chrome Pboobar Chrome Phoobar Chrome Phoobar Medium Uodget Medium Uodget Round Snaphoo Small Widget Small Widget 8 rows selected.



SQL> SQL> SELECT DISTINCT product_name 2 FROM plsqll81_purchase 3 ORDER BV product_nane; PRODUCT НЙИЕ



Chrone Phoobar Medium Uodget Round Snaphoo Small Widget SQL>



Рис. 3.11. Выбор уникальных значений из таблицы



74



ГлаваЗ



К тем же результатам приведет использование модификатора UNIQUE вместо DISTINCT, как в следующей команде: SELECT UNIQUE product_name FROM plsql!01_purchase ORDER BY product_name;



Модификаторы DISTINCT и UNIQUE дают одинаковый эффект, но DISTINCT более распространен, поэтому он и будет использоваться в этой книге. Выбор уникальных значений особенно полезен при наличии важного для вас критерия фильтрации. Например, чтобы увидеть, кто выполнял продажи в течение первой половины июля, можно применить следующую команду: SELECT DISTINCT FROM WHERE ORDER BY



salesperson plsql!01_purchase purchase_date BETWEEN 'Ol-JUL-03' AND 45-JUL-031 salesperson;



Выбор из DUAL В главе 2 вы видели, что над значениями из базы данных можно выполнять математические операции, определяя соответствующее выражение в операторе SELECT. В примерах той главы встречались следующие команды (которые теперь не работают, поскольку в таблице больше нет столбца SALES_TAX): SELECT FROM SELECT FROM SELECT FROM



product_name, product_price + sales_tax plsq!101_purchase; product_name, 100 - product_price plsqll01_purchase; product_name, sales_tax / product_price 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 не рассчитаны на непосредственное использование. Эта таблица предназначена для поддержки "моментальных" запросов, подобных только что выполненному. Попробуйте к ней обратиться, введя следующую команду:



Более сложные манипуляции с данными SELECT 18*1.05 FROM DUAL;



75 . " • . • '



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



Модификация данных в таблице Изменить данные в таблице Oracle очень легко. Используемая для этого команда называется UPDATE, а ее синтаксис выглядит следующим образом: UPDATE имя_таблицы SET имя_столбца = новое_значение WHERE условие; A Oiacle SQL'Plu file Edit Search flptions Help SQL> SELECT 18*1.85 FROM plsql181_purchase; 18*1.85 18.9 18.9 18.9 18.9 18.9 18.9 18.9 18.9



8 rows selected. SQL> DESC DUAL; Нам



Null?



Type UftRCHflR2(1)



DUMMV SQL> SELECT * FROM DUAL; D X



SQL> SELECT 18*1.85 FROM DUAL; 18*1.85 18.9



SQL> I



Рис. 3.12. Выбор значений из DUAL



•гага







Глава 3



76



Например, чтобы в таблице 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.



11



,-t Oracle SQL-Plus £8е £сй 'Search Options Help SQL> SELECT » FROM plsql101_purchase; QUflNTITY PURCHASE_ SAL



PRODUCT NAME



Small Widget Medium Uodget Chrome Phoobar Small Widget Medium Wodget Chrome Phoobar Round Snaphoo Chrome Phoobar



1 75 2 8 20 2 25 2



14-JUL-03 1U-JUL-83 -U-JUL-B3 15-JIIL-83 15-JUL-03 16-JUL-03 16-JUL-83 17-JUL-03



Cfl BB GO Gfl LB Си LB 88



8 rows selected. SQL>



SQL> UPDATE plsqliei_purchase 2 SET product name - 'Large Widget1 3 WHERE productfnane - •Small Widget'; 2 rows updated. SQL> SQL> SELECT •



FROM plsqliei_purchase;



PRODUCT NftHE



Large Uidget Medium Wodget Chrome Phoobar Large Uidget Medium Wodget Chrome Phoobar Round Snaphoo Chrome Phoobar



QUflNTITV PURCHASE, SAL



1 ni-jUL-вз та 75 2 8 20 2 25 2



1U-JUL-03 BB 1I|-JUL-B3 GA 15-JUL-03 GA 15-JUL-03 LB 16-JUL-03 CA 16-JUL-03 LB 17-JUL-03 BB



8 rows selected. SO,L> |



Рис. 3.13. Обновление данных в определенных записях



77



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



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



Удаление записей из таблицы Последний из фундаментальных навыков, необходимых при работе с таблицами, — это умение удалять записи. Команда DELETE имеет следующий синтаксис: DELETE FROM имя_таблщы WHERE условие; Эту команду легко использовать — возможно, даже слишком легко. Хорошо подумайте, какие условия вы будете в ней указывать!



Удаление записей, соответствующих заданному критерию Начнем эксперименты с этой командой с удаления самых новых записей из таблицы PLSQL101_PURCHASE. Введите следующую команду, чтобы удалить все записи о покупках, сделанных после 15 июля 2003 г.: SELECT * FROM plsqll01_purchase;



-



DELETE FROM plsql!01_purchase WHERE purchase_date > 45-JUL-031; SELECT * FROM plsqll01_purchase;



Результаты должны соответствовать показанным на рис. 3.14. Удаляя записи, можно указывать в условном выражении любой столбец (или столбцы). Например, следующая команда удалит записи, где упоминается "Large Widget": •



DELETE FROM plsqllOl_purchase WHERE product_name = 'Large W i d g e t ' ;



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



Удаление всех строк Как последний вариант можно удалить все строки таблицы. Для этого применяются два способа: использовать команду DELETE, не указывая условие WHERE, или использовать новую для вас команду TRUNCATE (Усечь).



Удаление записей без указания критериев



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



DELETE FROM имя_таблицы; Эта команда легко воспринимается при чтении кода, но у нее есть существенный недостаток: хотя она указывает на необходимость удаления всех записей без исключения, Oracle все равно будет сначала считывать каждую запись,



-



Глава3



78



* Oiacle SQL-Plus Fte Edit Search Qptions Це1р SQL> SELECT * FROM plsql181_purchase; PRODUCT



NflME



Large Widget Hediun Uodget Chrome Phoobar Large Widget Medium Uodget Chrome Phoobar Round Snaphoo Chrome Phoobar



QUANTITV PURCHftSE_ SflL 1 Ht-JUL-83 Cft 75 1U-JUL-83 BB 2 m-JUL-83 Gfl 8 15-JUL-ea Gfl 28 15-JUL-B3 LB 2 16-JUL-03 СЙ 25 16-JUL-83 LB 2 17-JUL-B3 BB



8 rows selected. SQL> SQL> DELETE FROM plsqllB1_purchase 1 2 WHERE purchase_date > 'IS-JUL-OS ; 3 rows deleted. SQL> SQL> SELECT « FROM plsql181_purchase; PRODUCT_NftME Large Widget Medium Uodget Chrome Phoobar Large Midget Mediun Uodget



QUANTITV PURCHftSE_ SflL 1 75 2 8 28



1U-JUL-83 1H-JUL-B3 Ht-JUL-83 15-JUL-B3 15-JUL-83



Cfl BB Gfl Gfl LB



SQL>



Рис. 3.14. Удаление записей по дате как и при наличии условия WHERE. Это может оказаться очень трудоемкой процедурой, напрасно отнимающей ваше время и ресурсы сервера. Поэтому, если нужно удалить из таблицы все записи, более эффективным методом будет использование команды TRUNCATE.



Усечение таблицы



Основное достоинство команды TRUNCATE — скорость. Когда Oracle выполняет эту команду, он не просматривает существующие записи, а просто выбрасывает их. Другой выигрыш от использования этой команды состоит в том, что она автоматически освобождает табличное пространство, которое занимали усеченные записи. Команда TRUNCATE имеет следующий синтаксис: TRUNCATE TABLE имя_таблицы; Чтобы увидеть ее в действии, усеките свою таблицу PLSQL101_PURCHASE, а затем просмотрите ее содержимое, использовав следующие команды:



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



79



^ 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



'







Глава 3



80



Сравните полученные результаты с показанными на рис. 3.15. В этом упражнении важно то, что первый оператор SELECT возвращает записи, тогда как второй — нет. Команда ROLLBACK удалила записи из локальной памяти, и они уже никогда не будут записаны в таблицу базы данных для постоянного хранения. В итоге все выглядит так, как будто вы вообще не вводили эти две записи. Возможности команды ROLLBACK не ограничиваются только одним уровнем отмены. В сочетании с другой командой, SAVEPOINT, она позволяет возвращаться в любую из предварительно установленных точек. Команда SAVEPOINT имеет следующий синтаксис: SAVEPOINT имя_точки_сохранения; Что дает использование нескольких точек возврата? Прежде всего, гибкость. При выполнении большого пакета команд можно устанавливать точки сохранения после каждой логической группы команд, и если следующая группа по какой-то причине даст неудовлетворительный результат, вы всегда сможете вернуться к последней точке и применить лишь те изменения, которые были выполнены до нее.



ВНЕ Be Edit Search Qptiora Help



SQL> INSERT INTO plsqll01_purchase VALUES 2 ('Small Widget 1 , 1, 'lii-JUL-вЗ1. ' C O ' ) ; 1 row created.



SQL> INSERT INTO plsqll01_purchase VALUES 2 ('Medium Wodgef . 75, '14-JUL-03*. 'BB'); 1 row created. SQL> SQL> SQL> SELECT * FROM plsqll01_purchase; PRODUCTJMHE



QUANTI TV PURCHASE. SAL



Snail Uidget Medium Uodget



1 1U-JUL-83 CA 75 14-JUL-03 BB



SQL> SQL> ROLLBACK;



Rollback complete. SQL> SQL> SELECT * FROM plsqllM_purchase; no rows selected,



,



SQL>



Рис. 3.15. Откат изменений данных



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



81



Чтобы увидеть все это в действии, вам предстоит ввести серию записей, помещая после каждой команды ввода точку сохранения. Затем вы будете выполнять откат к каждой из этих точек и наблюдать за тем, как это влияет на записи, возвращаемые операторами SELECT. Команды выглядят следующим образом: INSERT INTO plsqll01_purchase VALUES 1 ('Small Widget , 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 .-.,- ,:, , 1 ('Medium Wodget', 20, '15-JUL-03 , '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;



,



_



i. ,-



,



.



Обратите внимание, что имена точек сохранения в этом примере никак не систематизированы. Фактически каждое имя демонстрирует, как могла бы выглядеть эта система, если бы все точки сеанса назывались аналогичным образом. Имена точек сохранения — всего лишь метки, поэтому они могут быть любыми. У вас есть возможность выбирать имена, по которым будет видно, какие данные охватывает каждая точка. Для имен точек сохранения используются соглашения, похожие на соглашения для имен таблиц и столбцов: максимальная длина — 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 1 ('Chrome Phoobar', 2, '14-JUL-03 , '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 SELECT * FROM plsql101_purchase;



Команды для второго окна SQL*Plus SELECT * FROM plsq!101_purchase;



INSERT INTO plsql101 jDurchase VALUES ('Round Snaphoo', 5, '16-JUL-031, 'CA');



SELECT * FROM plsql101 purchase;



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 UflLUES 1 2 ('Snail Widget', 1, 'lij-JUL-вЗ , 'Cfl');



fj



1 row created. SQL> SAUEPOINT Й;







Sauepoint created. SQL> INSERT INTO plsqll01_purchase VALUES 2 С Medium Uodgef , 75, 1«i-JUL-e3 , 'BB'); 1 row created. SQL> SAUEPOINT B; Sauepoint created. SQL> INSERT INTO plsql101_purchase VALUES 1 2 ('Chrome Phoobar', 2, 'Ui-JUL-03 , 'Gfl'); 1 row created. SQL> SAUEPOINT C; Sauepoint created. SQL> INSERT INTO plsqll81 purchase UALUES 2 ('Small Widget'7 8, '15-JUL-03', 'EA'); 1 row created. SQL> SAUEPOINT D; Sauepoint created. SQL> INSERT INTO plsqll81_purchase UALUES 2 (-Medium Wodgef , 28, 'IS-JUL-вЗ', ЧВ1);



1 row created. SQL> SQL> COMMIT; Commit complete. 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 Snail Widget Medium Wodget Chrome Phoobar Small Widget Medium Uodget



QUANTITV PURCHASE_ SAL 1 75 2 8 2»



1i»-JUL-03 14-JUL-83 14-JUL-03 15-JUL-B3 1S-JUL-83



CA BB GA GA LB



SQL> |



Рис. 3.16. Завершение изменений в таблице



, .,



Глава3



84 КЩШ_0_Ш1



«-M-H_u_3L£ л



SgL> select • fro» PLSQL1 «.PURCHASE; PROOUCT.NAHE



QUANTITV PURCHASE. SAL



SHll Widget Hediun Wodget Chrone Phoobar SHll Widget Medium Wodget



1 75 2 1 21



sgL> sgi> SQL> sgi> sqi>



H-JUL-Ю 1»-JUL-«3 H-JUL-II3 15-JUL-n 15-JUL-03



• • • • • . • • • . • • • . С Ж Е ; ;:



Eh .£« S««*" awioni 'fcW>'



SQL> SOL>



. •: ' ' . - . ' ' .



sgL>



PROOUCT.NAHE



sgL>



SQL) insert into PLSQL1H PURCHASE ualue« ( 2 'Round SnaphoC, 5, '16-JU--2M3- . 'CA');



sgL> sgt> sgi>



• ;;



IQL> SQL>



1 ro» created.



gUANTITV PURCHASE. SAL 1 75 2 1 21



SQL>



.......... ,



.



, . ,



ш



.



.



.



.







i



__



SHll Widget Nediu« Wodget Chron* Phoobar SHll Widget Hediun Wodget







1 75 2 1 21



.' ;



: .- ;



PLSgL1H_PURCHRSE:



'RODUCT NAME



QUANTITY PURCHASE. SAL



SHll Widget HediuH Wodget сигом Phoobar SHll Widget Nediu» Wodget Round Snaphoo



;



' - ' '" (ч * Д ,



1 1J.-JUL-03 CA 75 K-JUL-n BB 2 1»-JUL-«3 GA 1 15-JUL-03 CA 21 15-JUL-03 LI 5 16-JUL-03 CA



6 rows selected.



••f.



CA II CA CA LI



$gt> SQL>



SQL> seltct • FroH







H-JUL-Ю 1»-JUL--3 -U-JUL-n 15-JUL-U 15-JUL-n . I, ,



\4L>



sgL>



.ii



'/ .



QUANTITY PURCHASE. SAL . — .. . . . . . .—



ни



Comit couple tt.



:^



CA BB CA CA LI



SQ->



PRUDUCT.NAME . . . . . . . . . . . . . .



sgL> con.it;



:



1J.-JUL-03 1»-JUL-»3 1»-JUL-e3 15-JUL-B3 15-JUL-.3



SQL> select • Fro» rLSQL1l1.PURCHASE;



S«L>



sgL> sgL> ML» sgL>



> ' ' •'•'.' :



sgi> select i Fran PLSQI1I1_P-RCHASE;



sgi> sgL>



JQL>



!



.: ' .



HP



SHll Widget • Mediun Wodget Cliron Phoobar SHll widget H-diun Wodget



SPL> 'S»L>



j . '•



', •



SQL) SQL> SQL>



CA IB CR CA LI



.• ,;; "



sgL> sgi>



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



:• • /•- • :



;fi



»«-> •I I



.;'S"



"".;",>



•':-



'



,



.



"-.-.".



.iH



•j



Рис. 3.17. Влияние команды COMMIT на другой сеанс базы данных



Явное и неявное завершение Выполняя предыдущие упражнения, вы ввели несколько команд COMMIT. Однако явный ввод команды—это лишь один из способов завершения изменений в базе данных. Некоторые команды Oracle выполняют завершение предыдущих команд, не дожидаясь ваших указаний, т.е. неявно. Говоря конкретнее, любые команды DDL (например, CREATE TABLE или DROP TABLE) неявно завершают изменение всех не сохраненных данных перед выполнением своих функций. Выход из Oracle (или просто закрытие SQL*Plus) также приводит к автоматическому завершению ваших изменений.



Итоги В этой главе было пройдено много базового материала. Сначала вы узнали, как с помощью конструкции WHERE оператора S ELECT ограничить диапазон



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



85



записей, возвращаемых Oracle. При фильтрации можно использовать разнообразные операторы сравнения, включая =, !=, >, =, = 100 AND last_stock_date IS NOT NULL ORDER BY product_name; P



2. В ответ SQL*Plus должен вывести сообщение "ORA-00942: table or view does not exist". Этим Oracle хочет сказать, что имя таблицы, из которой выбираются данные, записано неправильно. Вы должны повторить команду, но набирать шесть строк ради исправления двух опечаток — это слишком, поэтому... 3. Наберите edit и нажмите клавишу ENTER. Вы должны увидеть окно редактора, похожее на то, что показано на рис. 4.1. (Какая программа в действительности откроется для редактирования текста — зависит от



92



Глава4



Я afiedt.bul - Notepad



ЯИО!



ffc Ed» Swtch Help SELECT FROM WHERE



product_nnae plsq!101_produtc quantity_on_hand >- 101 AND



last_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



Лучший способ разобраться в данном подходе — это попробоватьего применить, а потом прочитать объяснение. Выполните следующие действия:



93



Управление SQL*Plus 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/name



1* SELECT product_nane FROM plsql1B1 product SQL> / PRODUCT NflME



Snail Widget Hediun Wodget Chrome Phoobar Round 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 FROM WHERE



product_nmae plsql!01_produtc quantity_on_hand >= 100 AND last_stock_date IS NOT NULL ORDER BY product_name; г



c/ma/am 2



c/tc/ct



/



Сравните свои результаты с показанными на рис. 4.3. Как видите, при каждом вводе числа SQL*Plus делает строку из предыдущей команды с этим номером текущей и позволяет ее модифицировать при помощи команды CHANGE-.



Управление SQL* Plus



95



Ж Oracle SQL'Plus File Edit Seaich Qptions Help SQL> SELECT pi-oduct_nmae 2 FROM plsql181_pi-odutc



3 WHERE t



quantity_on hand >- 188 AND



5 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* SQL> 1» SQL> 2» SQL> 2* SQL>



1 SELECT c/na/an SELECT 2 FROM c/tc/ct FROM /



product_nmae product_name plsql1B1_produtc plsqll01_product



PRODUCT NfltlE



Chrome Phoobar Medium Uodget SQL>



Рис. 4.З. Использование команды CHANGE для многострочного редактирования



Копирование и вставка Довольно часто требуется повторять SQL-команду, выполненную две, три и даже большее число команд назад. В таких случаях команды CHANGE и EDIT не помогают, поскольку нужная команда больше не находится в буфере команд SQL*Plus. Однако есть другой способ воспользоваться ранее введенными строками. Вы можете копировать команды с экрана SQL*Plus и повторно использовать их, вставляя (paste) в строку приглашения SQL>. Чтобы увидеть, как это делается, начните со ввода следующих команд: SELECT * FROM plsql!01_product; UPDATE plsql!01_product SET product_name = 'Large Widget1 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 Help SQL> SELECT * FROM plsqll01_product; PRODUCT_NAME



ИИЕЗ



PRODUCT_PRICE QUAHTITV_OH_HAHO LAST_STOC



Snail Uidget Medium Uodget Chrome Phoobar



99 75 50



Round phrone Snaphoo



25



1 15-JAN-03 1000 15-JAN-02 100 15-JAN-03



10000



SQL>



SQL> UPDATE pisqnoi_product 2



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-B3



Medium Uodget Chrome Phoobar Round Chrome Snaphoo



75 50 25



1000 15-JAN-02 100 15-JAN-03 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.



Глава 4



98



Oracle SQL-Plus Are you sure you want to clear the Screen and the Screen Buffet?



Cancel



O'K"



Рис. 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 autocommit autoprint autotrace blockterminator cmdsep colsep compatibility со neat copycommit copytypecheck



'U



jraiuu С Delault current



1



Buffer Width:



J100



Buffer Length:



J1000



j ,



....



i 1



Г On



f Off 1 1



Cancel



|



j



Рис. 4.6. Диалоговое окно настройки среды SQL*PluS



OK



I



Управление 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



99



Small Widget 15-JflN-03



1



'','•: Medium Uodget 15-JflN-02



Chrome Phoobar 15-JflN-83 PRODUCT_HftME



75



-



.



1000 '



50



"



I



.



100



PBODUCT_PRICE QUflNTITY_ON_HAND



LflST_STOC



Round Chrome Snaphoo



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; PRODUCT_NAME Small Widget Medium Uodget Chrome Phoobar Round Chrome Snaphoo



НЕС .d



PRODUCT_PRICE QUANTITV_ON_HAND LflST_STOC 99 75 50 25



1 15-JAN-03 1000 15-JAN-02 100 15-JAN-03 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



BflLoOL^J



^£ite Edit Search Options Help ! SQL> SELECT « FROM plsq!101_product ; PRODUCT_HAME



PRODUCT_PRICE QUANTITV_ON_HflND LAST_STOC



Small Widget Medium Uodget Chrome Phoobar Round Chrome Snaphoo



99 75 50 25



1 15-JflN-03 1008 15-JAH-02 100 15-JAH-03 10000



. , '



d



SQL> SQL> COLUMN product price FORMAT 9999.99 SQL> SQL> SELECT * FROM plsqll Byproduct ; PRODUCT_NAME Small Uidget Medium Wodget Chrome Phoobar Round Chrome Snaphoo



-



PRODUCT_PRICE QUANTITV_ON_HflND LAST_STOC 99. BB 75.08 50.08 25. OB



1 15-JAN-83 1000 15-JAN-02 188 15-JAN-03 18888



SQL> .



.



:



'.



..



.-



:



• ,.



'



&"••



^iJ



-~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. Коды числовых форматов ;



Элемент



Пример



Описание



$



$9999



Помещает знак доллара перед значением



,(запятая)



9,999



Помещает запятую в указанной позиции



. (точка)



99.99



Помещает десятичную точку в указанной позиции



Ml



9999MI



Отображает знак минуса (-) после любого отрицательного числа



ЬУУУУ



Помещает в указанной позиции знак плюса (+) для положительных чисел и знак минуса (-) для отрицательных



PR



9999PR



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



D



99D99



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



9G999



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



С999



Отображает в указанной позиции знак денежной единицы ISO



L999



Отображает в указанной позиции знак местной денежной единицы



RN или rn



RN



Отображает числа римскими цифрами верхнего или нижнего регистра (только для целых чисел от 1 до 3999)



О



0999



Отображает один и более начальных нулей



О



Отображает пустые значения в виде нулей



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;



. ,.



Глава 4



106



Ж Oiacle SQL-Plus File Edit Seach Options Help SQL> SELECT * FROM plsq!1B1_prodUCt; Product Name



PRODUCTJ>RICE QUANTITV_ON_HAND LAST_STOC



Snail Widget



$99.00



1 15-JAN-B3



Mediun Wodget



$75. ее



1,888 1S-JflN-82



Chrome



$58.80



188 15-JAN-B3



Round



$25.80



Phoobar Chrome Snaphoo



1(1,880



SQL> SQL> SQL> SQL> SQL> SQL> SQL>



COLUMN COLUMN COLUMN COLUMN



product nane FORMAT A10 WORD_URAP HEADING 'Name' JUSTIFV CENTER product_price FORMAT $99.99 HEADING 'Price1 JUSTIFV RIGHT quantity_on_hand FORMAT 99,999 HEADING 'On|Hand' JUSTIFV RIGHT last_stock_date HEADING 'Last(Stock(Date' JUSTIFV RIGHT



SELECT » FROM plsq!181_product;



Nane



Price



On Hand



Last



Stock Date



Small Widget



$99.BB



1 15-JAN-B3



Mediun Uodget



$75.88



1,888 1S-JAN-02



Chrome Phoobar



$58.88



188 15-JAN-B3



Round Chrome Snaphoo



$25.BO



18,088



SQL> JJJ



Рис. 4.10. Форматирование текста с использованием команд COLUMN Для отключения форматирования, заданного командой COLUMN, используется следующий синтаксис: COLUMN имя_столбца OFF Например, чтобы столбцы таблицы PLSQL101_PRODUCT приняли тот вид, который они имели до форматирования, введите следующие команды:



Управление SQL'Plus COLUMN COLUMN COLUMN COLUMN



107



product_name OFF product_price OFF quantity_on_hand OFF 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 Manager и перейдите в каталог, где вы сохранили файл 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 VARCHAR2(15), last_name VARCHAR2(25)



INSERT INTO plsqll01_temp VALUES ( ' J o e ' , ' S m i t h ' ) ; INSERT INTO plsq!101_temp VALUES ( ' J a n e ' , ' M i l l e 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). Переменная заменяет часть команды, позволяя вводить эту часть при выполнении сценария и тем самым влиять на его



mwss



-Г 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. FIRST NAME



LAST NAME



Joe Jane



Smith Miller



Table dropped. SQL>



Рис. 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_purchase WHERE 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 и SET ECHO 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_date FROM plsql!01_purchase WHERE quantity SELECT « FROM plsqllB1_purchase 2 UHERE purchase_date BETWEEN (SVSDATE-30) ftND SVSDfiTE; PRODUCT НЙМЕ



ЯМЕ



QUflNTITV PURCHASE. SflL



Snail Uidget Medium Uodget Round Snaphoo



10 05-JUL-BO SH 15 21-JUN-B8 SH 25 28-JUN-Oe SH



SQL>



Рис. 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 FROM SELECT FROM



product_name, product_price plsql!01_product; product_name, ROUND(product_price, plsql!01_product;



0)



Полученные результаты должны выглядеть так, как показано на рис. 5.2. Этот пример показывает один из наиболее распространенных способов применения функции ROUND. Другое применение — округление слишком точных значений, содержащих много знаков после десятичной точки, до долларов и центов. Для этого потребуется указать точность, равную 2. Можно ука* Oracle SQL-Plus File Edit Seaich Options Help SQL> SELECT product_nane, product_price 2 FROM plsql1B1_product; PRODUCT NflHE Small Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar



PRODUCT PRICE



»



99 75 50 25 9.95



SQL> SQL> SELECT product•name, ROUND(product_price, 0) 2 FROM plsqll Byproduct; PRODUCT_HflME Small Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar + SQL> |



Рис. 5.2. Функция ROUND



ROUND(PRODUCT_PRICE,8) 99 75 se 25 10



Встроенные функции SQL



123



/



зывать любое число десятичных знаков, но некоторые значения имеют больше смысла, чем другие. Если указать отрицательное число, то функция ROUND начнет округление до десятичной точки, иначе говоря, числа будут округляться до ближайших десятков, сотен, тысяч и т. д. Лучший способ увидеть связь между точностью, задаваемой в функции ROUND, и изменением, которое производится в обрабатываемом числе, — это применить функцию к числам, содержащим много десятичных знаков. Поскольку ваши тестовые таблицы не содержат записей с такими числами, да и нет никакого смысла создавать подобные записи, мы воспользуемся таблицей DUAL. Введите перечисленные ниже команды, чтобы посмотреть, как отражается количество десятичных знаков, указанных в функции ROUND, на способе округления переданного ей числа. Результаты сведены в таблицу 5.1. SELECT SELECT SELECT SELECT SELECT SELECT SELECT SELECT



ROUND(1234.5678, ROUND(1234.5678, ROUND(1234.5678, ROUND(1234.5678, ROUND(1234.5678, ROUND(1234.5678, ROUND(1234.5678, ROUND(1234.5678,



4) FROM DUAL; 3) FROM DUAL; 2) FROM DUAL; 1} FROM DUAL; 0) FROM DUAL; -1) FROM DUAL; -2) FROM DUAL; -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 SELECT SELECT SELECT SELECT SELECT SELECT SELECT SELECT



Глава 5 TRUNC(1234 .5678, TRUNC (1234 .5678, TRUNC(1234 .5678, TRUNC(1234 .5678, TRUNC(1234 .5678, TRUNC(l-234 .5678, TRUNC(1234 .5678, TRUNC(1234.5678,



4) FROM DUAL; 3) FROM DUAL; 2) FROM DUAL; 1) FROM DUAL; 0) FROM DUAL; -1) FROM DUAL; -2) FROM DUAL;



-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 A Oiacle SQL'Plus £ite £А iearch Qptions fclelp SQL> SELECT UPPER(product_nane) FROM plsq!101_product;



125 ВНЕ



UPPER(PHODUCTJtt ME) SMflLL UIDGET MEDIUM UODGET CHROME PHOOBflR ROUND CHROME SNAPHOO EXTRA HUGE MEGA PHOOBAR + SQL> SELECT LOWER(product_nane) FROM plsql101_product; LOWER(PRODUCT_NAHE)



snail midget medium wodget chrome phoobar round chrome snaphoo extra huge mega phoobar + SQL> SELECT IHITCAP(product_nane) FROM plsql101_product; INITCAP(PRODUCT_NAME)



Small Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra 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 SET WHERE SELECT WHERE



plsqll01_product product_name = 'chrome phoobar' product_name = 'Chrome Phoobar'; * FROM plsql!01_product UPPER(product_name) LIKE '%PHOOBAR%';



Глава 5



126



У, Oiacle SQL-Plus File



Edit



Search



Options Help



SQL> SELECT * FROM plsqllB1_prodUCt 2 WHERE UPPER(product_name) LIKE ' PRODUCT NflHE



PRODUCT_PRICE QUflNTITV_OH_HflND LflST_STOC



chrome phoobar



Extra Huge Mega Phoobar



50 9.95



188 15-Jf)N-03 1234 15-JflN-84



SQL>



Рис. 5.4. Использование функции UPPER для упрощения поиска текста Используя функцию UPPER таким образом, можно значительно облегчить поиск текста, когда неизвестно, в каком регистре он был введен. Однако, как показано на рис. 5.4, выходные данные по-прежнему остаются несогласованными в части использования символов верхнего и нижнего регистров. Чтобы привести значения столбца PRODUCTJStAMEK единому виду, нужно применить к нему функцию INITCAP. Введите следующий код: SELECT INITCAP(product_name),



product_pr,ic,e, quantity_on_hand, FROM WHERE



last_stock_date plsql!01_product 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_LENGTH FROM plsql!01_product WHERE LENGTH(product_name) > 15 ORDER BY product_name;



127



Встроенные функции SQL Ж Oracle SQL'Plus File Edit Seach Options Help SQL> SELECT INITCAP(product_name), 2 product price, 3 quantity_on_hand, SELECT SUBSTR(itemjld. 1. 2) MFGR_LOCflTIOH, 2 SUBSTR(item_id, 4, 3) ITEH_NUHBER,



3 4 FROM



5 ;



iten_desc plsql101_old item



MF ITE ITEM DESC Lfl 1B1 Can, Snail LA 102 Can, Large



Lfl 183 Bottle, Small



Lfl NY NV NY NV



104 Bottle, Large 101 Box, Snail 162 Box, Large 103 Shipping Carton, Snail 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 / i



FROM



1 ) plsqll01_old_item;



Как показано на рис. 5.7, функция INSTR возвращает число, обозначающее положение запятой в каждом описании изделия. Мы могли бы использовать это число в качестве длины подстроки в функции SUBSTR, чтобы получить категорию изделия, но если так поступить, то в категорию будет включена запятая. Следовательно, нам нужно вычесть 1 из значения функции INSTR. Это демонстрируется в следующем коде: SELECT item_desc, SUBSTR (item_desc, 1, INSTR (item_desc,



FROM



) -1 . ) plsql!01_old_item;



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



131



Встроенные функции SQL



НИИ



Ж Oracle SQL'Plus File Edit Search Qptbns tjelp SQL> SELECT item_desc, 2 INSTR(iten_desc,



3 U 5



1 )



..,



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



* 7 7



Рис. 5.7. Результаты, возвращаемые функцией INSTR



* Oracle SQL'Plus Fte Edit Search Options Help SQL> SELECT item_desc, 2 SUBSTR(item_desc,



3



1,



4 5 6



7 8 9



IHSTR(iten desc, 1



) -1



FROM



)



plsql1B1_old_iten;



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



Can Can Bottle Bottle Box Box Shipping Carton Shipping carton



8 rows selected. SQL> |



iLJ Рис. 5.8. Выделение подстроки переменной длины



Я -IE



132



Глава5



Вы только что получили первый опыт использования одной функции внутри другой. Это называется вложением (nesting) функций. Внутренняя функция возвращает значение, которое затем используется внешней функцией. Это весьма мощное средство. Но вернемся к нашей задаче — ведь из столбца ITEM_DESC еще не извлечен размер изделия. Вся хитрость в том, чтобы найти начальную позицию размера, требуемую для функции SUBSTR. Решение опять обеспечивает функция INSTR, определяющая положение запятой. Но фактическое начало подстроки с размером сдвинуто относительно запятой на два символа, поэтому нужно добавить 2 к значению, возвращаемому функцией INSTR. Введите следующий код, чтобы получить список размеров изделий: SELECT item_desc, SUBSTR(item_desc, INSTR(item_desc, i t



1 FROM



Г



I



) +2, 99 ) 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,



FROM



99 ) ITEM_SIZE plsql!01_old_item;



Результаты выполнения этой команды показаны на рис. 5.9. Отступы, использованные в только что показанном коде, поясняют, как сгруппированы части каждой функции. Такое форматирование полезно при изучении функций, поскольку оно помогает определить, с какой частью той



Встроенные функции SQL



133



Bra в



К * Oiacle SQL'Plus File Edit Search Qplions Help SQL> SELECT item desc, 2 SUBSTR(item desc, 3 1. 4 INSTR(item desc,



5



6 7 8



9



10 11



1 ) -1 ) CATEGORY,



SU8STR(item desc,



IHSTR(item desc.



12



1



13 14 15



) +2,



16 FROM



d



99 ) ITEM SIZE



.



plsq!101_old_itero;



ITEM_DESC



CftTEGORY



Can, Small Can, Large Bottle, Small Bottle, Large Box, Small Box, Large



Can Can Bottle Bottle Box Box



Shipping Carton, Small



Shipping Carton, Large 8 rows selected.



Shipping Carton



Shipping Carton



ITEM_SIZE



Small



Large Small Large



Small



Large Small Large



, . . . , . . ,



SQL> |



.iLJ



26.



Рис. 5.9. Разбор на несколько подстрок переменной длины или иной функции вы имеете дело в данном месте команды. Для Oracle не имеет значения, как отформатирована команда, поэтому следующий код, представляющий собой ту же команду в более компактном виде, даст идентичные результаты: SELECT item_desc, SUBSTR(item_desc, 1, INSTR(item_desc, ',', 1) -1) CATEGORY, SUBSTR(item_desc, INSTR(item_desc, ',', 1) +2, 99) ITEM SIZE FROM plsql!01_old_item;



Когда ниже в этой главе мы будем изучать копирование записей из одной таблицы в другую, у вас будет еще возможность поработать с подстроками. LTRIM и RTRIM Лучший способ объяснить, что делают эти функции, — привести пример проблемы, которую они решают. Введите следующий код и сравните результаты с показанными на рис. 5.10.



Глава 5



134



IJH* Oracle SQL'Plus File 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 Item Item Item Item Item Item Item Item



Lfl-101 Lfl-1 02 Lfl-1 03 Lfl-1 04 NV-101 NV-1 02 NV-103 NV-1 Oil



is is is is is is is is



described described described described described described described described



as as as as as as as as



a a a a a a a a



Can, Small Can, Large Bottle, Small Bottle, Large Box, Small Box, Large Shipping Carton, Small Shipping Carton, Large



8 rows selected. SQL> | jJJ



--



j5 1



Рис. 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



Ч 5 6



'



| |



RTRIM(item_id) ||



2 3 FROM



и



' is described as a RTRIM(iten_desc) || '.• "Item Description Sentence" plsql101_old_iten;



Item Description Sentence Item Item Item



LA-101 is described as a Can, Small. LA-1D2 is described as a Can, Large. LA-ЮЗ is described as a Bottle, Small.



Item Item Item Item



NV-101 NV-102 NV-103 NV-1B4



Item Lfl-184 is described as a Bottle, Large. is is is is



described described described described



as as as as



a a a a



Box, Snail. Box, Large. Shipping Carton, Small. Shipping Carton, Large.



8 rows selected. SQL>



Рис. 5.11. Обрезание текстовых значений фиксированной длины



136



Глава 5



File Edit Seated Option: Help Item ID sentence



The The The The The The The The



Iten Iten Iten Iten Iten Iten Iten Iten



ID ID ID ID ID ID ID ID



for for for for for for For for



Snail Large Snail Large



Can is: 1.Й-101. Can is: Ld-102. Bottle is: Lft-183. 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.



8 rows selected. SQL>



Рис. 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 SELECT SELECT SELECT



ADD_MONTHS('28-NOV-00', 1 ADD_MONTHS('29-NOV-OO , ADD_MONTHS('30-NOV-00', ADD_MONTHS('31-DEC-00',



1) FROM DUAL; 1) FROM DUAL; 1) FROM DUAL; -1) FROM DUAL;



Обратите внимание, что в последней команде месяц вычитается из даты "31 декабря". Формально ответом будет "31 ноября", но в ноябре только 30 дней. Функция ADD_MONTHS это знает и учитывает при выдаче окончательного результата. Такая корректировка выполняется только для последнего дня месяца. Как видно по трем предыдущим командам, в остальных случаях ADD_MONTHS добавляет ровно один месяц. Ж Oiacle SQL-Plus fife £dk !Seach SQL> 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 SELECT SELECT SELECT



LAST_DAY(SYSDATE) FROM DUAL; LAST_DAY('01-JAN-02') FROM DUAL; LAST~DAY('15-JAN-02') FROM DUAL; 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');



Глава 5



140



Теперь, имея подходящую таблицу, вы можете убедиться в полезности функции 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 Help SQL> SELECT f irst_nane , 2 lastjname, 3 hire date, * LAST~DflV(hire_date)+1 plsqll81_person; 5 FROM



d INSURflNCE_START_DATE



FIRST NAME



LftST_NflME



HIRE DATE INSURANCE



Charlene Gary Bobby Laren



Atlas Anderson Barkenhagen Baxter



B1-FEB-82 15-FE8-82 28-FEB-82 ei-HflR-82



81-MAR-82 81-MAR-B2 B1-MAR-B2 81-APR-82



SQL>



Рис. 5.14. Использование функции LAST_DAY для определения первого дня следующего месяца Вкладывая функции для работы с датами друг в друга, можно получить довольно интересные результаты. Рассмотрим такой пример: каждая запись в таблице PLSQL101_PRODUCT содержит дату последнего пополнения запаса товара на складе. Допустим, в некой компании, где вы работаете, пополнение запасов должно производиться каждые три месяца. Чтобы получить очередную дату, можно было бы использовать функцию ADD_MONTHS, но есть осложняющее обстоятельство: товары заказываются только первого числа каждого месяца. Таким образом, сначала вам нужно отсчитать три месяца вперед, а потом перейти на начало следующего месяца. Как это сделать? Очевидно, объединив два только что изученных приема: добавление заданного числа месяцев к дате и использование функции 1А8Т_ОА¥для получения последнего дня месяца с последующим добавлением к нему единицы. В итоге синтаксис вложенных функций примет следующий вид:



141



Встроенные функции SQL 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_product WHERE quantity_on_hand SELECT product_nane, 2 quantity_on_hand,



3



last_stock_date,



Ц 5



FROM



LAST DftV(ftDD_MONTHS(last_stOCk_date, 3))+1 RESTOCK_DflTE plsqll81_product



7



ORDER BV product_nane;



6 WHERE quantity_on_hand |



-iLJ



Рис. 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, FROM DUAL;



'MONTH DDTH 1 )



При добавлении суффикса SP день будет представлен в буквенном написании. В качестве примера введите следующий код: SELECT TO_CHAR(SYSDATE, 'MONTH DDSP') FROM DUAL;



В результате объединения этих двух суффиксов Oracle будет расшифровывать день месяца и добавлять после него "st", "nd", "rd" или "th". Это демонстрируется следующей командой: SELECT TO_CHAR(SYSDATE, FROM DUAL;



'MONTH DDSPTH 1 )



Как вы могли заметить по трем последним примерам, внешний вид отображаемых дат можно еще немного улучшить. Например, элемент формата MONTH всегда дополняет название месяца до девяти символов, в результате чего возникает большой промежуток между месяцем и числом, если месяц имеет короткое название. Кроме того, вся дата записывается прописными буквами, что придает ей несколько "кричащий" вид. Эти проблемы можно решить с помощью других уже известных вам функций. Полный список "косметических" проблем таков: • Лишние пробелы после названий месяцев 6 Зак. 725



144



Глава5



Таблица 5.3. Элементы кода формата даты и времени, используемого в функции TCLCHAR Элемент



Значение



/



Воспроизведение соответствующих знаков препинания и текста в кавычках



'любой текст' AD АО.



Обозначение года новой эры (с точками или без точек)



AM А.М.



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



ВС Р В.L».



Обозначение года до новой эры (с точками или без точек)



СС SCC



Век (для четырехзначного года); в варианте с "S" даты до н. э. предваряются знаком"-"



D



День недели (1-7)



DAY



Название дня недели, дополненное пробелами до девяти символов



DD



'



'•



!



,'.. .



'•



.'



День месяца (1-31)



:-



•",



'



..



DDD



День года (1-366)



DY



Сокращенное название дня недели



Е



Сокращенное название эпохи (для японского имперского, тайско-буддийского и официального китайского календарей)



ЕЕ



Полное название эпохи (для японского имперского, тайско-буддийского и официального китайского календарей)



НН



Часдня(1-12)



НН12 НН24 IW



.



Часдня(1-12) Час дня (0-23) Неделя года (1 -52 или 1 -53) согласно стандарту ISO



Встроенные функции SQL



145



Таблица 5.3 (продолжение) Элемент



Значение



IYYY



Четырехзначный год согласно стандарту ISO



IYY



Последние цифры (три, две или одна) года ISO



- - ...



IY



. -



J



Юлианский день; количество дней, прошедших с 1 января 4712 г. до н.э. Число со спецификатором J должно быть целым •



Ml .



Минуты (0-59)



М



Месяц (01-12; январь = 01)



MON



Сокращенное название месяца



MONTH РМ P.M.



. • . - • • ; • . • : • : .1



Название месяца, дополненное пробелами до девяти символов Обозначение времени после полудня (с точками или без точек) /



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 SYEAR



)



Значение



. • Год в буквенном написании; в варианте с "S" даты до н.э. предваряются знаком"-"



YYYY SYYYY



Год из четырех цифр; в варианте с "S" даты до н.э. предваряются знаком"-"



YYY YY Y



Последние цифры года (три, две или одна)



• Прописные буквы в названиях месяцев • Прописные буквы в суффиксе после числа месяца (например, "ТН" вместо "th"). • Прописные буквы в числе месяца при использовании суффикса SP Давайте посмотрим, как использовать ранее изученные функции для решения каждой из этих проблем. Сначала мы займемся исправлением следующего формата даты: SELECT TO_CHAR(SYSDATE, FROM DUAL;



'MONTH DDTH 1 )



Чтобы удалить избыточные пробелы после названия месяца, можно воспользоваться функцией RTRIM. При этом, однако, потребуется разделить месяц и число, что легко сделать, выбрав их как два отдельных значения и применив операцию конкатенации: Приведенная ниже команда показывает, как отделить название месяца от числа. Введите ее, и вы обнаружите те же выходные данные, которые выводились предыдущей командой. SELECT TO_CHAR(SYSDATE, I



'MONTH')



||



' I I



TO_CHAR(SYSDATE, FROM DUAL;



'DDTH')



Теперь можно заняться удалением лишних пробелов. Подставьте функцию TO_CHAR в функцию RTRIM, как показано ниже: SELECT RTRIM(TO_CHAR(SYSDATE, ' M O N T H ' ) ) I I I I TO_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 LOWER ( TO_CHAR ( S YSDATE , ' DDTH ' ) ) FROM DUAL;



I I



Чтобы окончательно освоить только что продемонстрированную технику, улучшите внешний вид даты, отображаемой следующей командой: SELECT TO_CHAR(SYSDATE, FROM DUAL;



'MONTH DDSPTH')



Форматирование числовых значений Функция 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 преобразует текстовое представление даты (и/или времени) в действительные значения даты/времени. Хотя ее основное назначение — импорт текстовых файлов с датами и временем из других баз данных, она также полезна при ручном вводе даты в формате, отличном от стандартного



Глава5



148 Таблица 5.4. Элементы кода формата чисел Элемент



Пример



Описание



$



$9999



Помещает знак доллара перед значением



,(запятая)



9,999



Помещает запятую в указанной позиции



.(точка)



99.99



Помещает десятичную точку в указанной позиции



Ml



9999MI



Отображает знак"-" после отрицательного числа



S9999



Помещает в указанной позиции знак"+" (для положительных чисел) или знак"-" (для отрицательных чисел)



9999PR



Окружает отрицательные числа



99D99



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



PR



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



G С



С999



Отображает знак денежной единицы ISO в указанной позиции



L999



Отображает знак местной денежной единицы в указанной позиции



RN или rn



RN



Отображает числа римскими цифрами верхнего или нижнего регистра (только для целых чисел от 1 до 3999)



О



0999



Отображает один и более начальных нулей



О



9990



Отображает пустые значения как нули



формата Oracle (DD-MON-YY), или при вводе времени. Эта функция имеет следующий синтаксис: ТО_ОАТЕ(входное_значение, код_формата) Функция TO_DATE использует подмножество элементов кода формата, определенных в функции TO_CHAR. Элементы, входящие в это подмножество, перечислены в таблице 5.5.



149



Встроенные функции SQL Таблица 5.5. Элементы кода формата даты и времени Элемент



Значение



/



Воспроизведение соответствующих знаков препинания и текста в кавычках



'любой текст' AD A.D.



Обозначение года новой эры (с точками или без точек) Обозначение времени до полудня (с точками или без точек)



А.М. ВС В.С.



Обозначение года до новой эры (с точками или без точек)



D



День недели (1-7)



DAY



Название дня недели, дополненное пробелами до девяти символов



DO



День месяца (1-31)



ODD



День года (1-366)



DY



Сокращенное название дня недели



HH



Час дня (1-12)



HH24



Час дня (0-23)



J



Юлианский день; количество дней, прошедших с 1 января 4712 г. до н.э. Число со спецификатором J должно быть целым



Ml



Минуты (0-59)



MM



Месяц (01-12; январь = 01)



MON



Сокращенное название месяца



MONTH



Название месяца, дополненное пробелами до девяти символов



PM P.M.



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



'



...;



.



Глава 5



150



Таблица 5.5



(продолжение)



Элемент



Значение



RM



Римский номер месяца (I-XII; январь = I)



RR



Для года, записанного двумя цифрами, возвращает год следующего века, если год < 50 и две последние цифры текущего года >= 50; возвращает год предыдущего века, если год >= 50 и две последние цифры текущего года < 50



RRRR



На входе допустимы четыре или две цифры. Для двух цифр возвращает тот же результат, что и RR. Если вам не нужна эта возможность, просто вводите четырехзначный год



SS



Секунды (0-59)



SSSSS



Секунды после полуночи (0-86399)



Y.YYY



Год с запятой в указанной позиции



YYYY ' SYYYY YYY YY Y



.



'



Год из четырех цифр; в варианте с "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_product SET 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;



151



Встроенные функции SQL



• J: Oracle SQL-Plus File Edit Search Options Help SQL> SELECT product name, 2 TO_CHAR(product_price, '$9,999.88') "Price", 3 quantity on_hand, 4 last stock date 5 FROM plsq!181_product; PRODUCT_NftME Small Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar + Square Zinculator



Price



• -Ш1 x|



d



QUANTITV_ON_HAHD LAST_STOC



$99.88 $75.88 $58.88 $25.88 $9.95 $45.88



1 1888 100 18888 1234 1



15-JAN-83 15-JAN-82 15-JAN-83 15-JAN-84 89-JUL-88



V



6 rows selected. SQL> •







лП



iLJ



Рис. 5.16. Использование функции TO_CHAR для улучшения внешнего вида чисел



ДИДДН I' 'Iv|



• *. Oracle SQL-Plus File £dit Search Qptions Help PRODUCT_NAME Small Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar + Square Zinculator 6 rows selected.



.



Price $99 .88 $75 .88 $58 .88 $25 .88 $9 .95 $45 .88



On Hand Last Stocked 1 1,888 188 18,000 1,234 1



JAN 15, 2883 JAN 15, 2882 JAM 15, 2883



-



JAN 15, 2884 JUL 89, 2888 ,



SQL> jJJ



.



J



Рис. 5.17. Выходные данные, отформатированные различными функциями TO_CHAR



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



152



Глава5



A Oracle SQL-Plus fte Ed* !£e«ch :ДрИога:Ые1р SQL> SELECT product_nane,



2 3 it 5 FROM



И RES r



;;;



V



'



product_price. quantity_on hand, TO CHARUast stock date, 'MM-DD-YVVV НН24:МГ) "Last Stocked" plsql101_product;



PRODUCT_NftME



Small Widget Medium Wodget Chrone Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar Square Zinculator



PRODUCT_PRICE QUfiNTITV_ON_HflND Last Stocked



99 75 50 25 9.95 45



1 1B00 100 10000 . 1234 1



01-15-2003 BO:08 01-15-2802 00:00 01-15-2003 00:00 01-15-2004 00:00 07-09-2000 00:00



6 rows selected. SQL> SQL> UPDATE plsqHQl product 2 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



Small Widget Medium Wodget Chrome Phoobar Round Chrbme Snaphoo Extra Huge Mega Phoobar Square Zinculator



PRODUCT_PRICE QUAHTITV_ON_HAND Last Stocked



99 75 50 25 9.95 45



1 1000 100 10000 1234 1



01-15-2003 00:00 01-15-2002 00:00 01-15-2003 00:00 01-15-2004 00:00 12-31-2002 23:3B



б rows selected. SQL>



Рис. 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 и поручено выбрать из нее некоторый набор записей. Однако со времени использования старых изделий структура вашей компании изменилась, и теперь вместо идентификации изделий по городу, откуда они поступили, необходимо показывать регионы. Изделия, 1 идентификатор которых содержит "NY , относятся к восточному региону ("Eastern"), а изделия "LA" поступают из западного ("Western") региона. Трехзначные номера, следующие за сокращенными названиями городов, по-прежнему должны отображаться, поэтому вам придется использовать функцию SUBSTR, чтобы выделять подстроки с этими номерами. Ниже показано, как использовать функцию DECODE для трансляции городов в регионы. Просмотрите и запустите этот код. Результаты должны совпасть с показанными на рис. 5.19.



Глава 5



154



Щ * Oiacle SQL-Plus



ННИЕ



File Edit Search 'Options Help SQL> SELECT DECODE(SUBSTR(iten_id, 1,2), 2 'Lfl 1 , 'Western' , 'NV , 'Eastern' , 3 4 5 6 7



8



FROM



-d -J



'* Unknown »' ) "Region", SUBSTR(iten_id, 4,3) "Item ID", item desc plsql181_old_iten;



Region



Ite ITEMJDESC



Western Western Western Western Eastern Eastern Eastern Eastern



181 Can, Snail 182 Can, Large 183 Bottle, Snail 184 Bottle, Large 181 Box, Snail 182 Box, Large



183 Shipping Carton, Snail 104 Shipping Carton, Large



-



8 rows selected. SQL> |







'



:



jJJ



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



Рис. 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 JHelp SQL> SELECT » FROM plsql101_product; PBODUCT_NftME



PRODUCT_PRICE QUflNTITV_OH_HflND LAST_STOC 99 75 50 25



Small Widget Medium Uodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar Square Zinculator



9.95



45



1 1000 100 10000 1234 1



15- JAN- 03 15-JftN-B2 15-JAN-83



15-JflN-flJt 31 -DEC- 02



6 rows selected. SQL> — This line will be ignored. Oracle will not try to run it. SQL> SELECT « FROM plsql1D1_purchase; PRODUCT_NAME



Small Widget Medium Uodget Chrome Phoobar Small Widget Medium Uodget Round Snaphoo



QUANTITY PURCHASE. SAL 1 75 2 8 20 5



14-JUL-03 CA 14-JUL-03 BB 14-JUL-03 GA 15-JUL-03 GA 15-JUL-03 LB 16-JUL-03 CA



6 rous selected. SQL>



Рис. 5.22. Однострочный комментарий в SQL-сценарии Этот сценарий демонстрирует использование многострочных комментариев. Он написан специально для книги PL/SQL 101 издательства Oracle Press. */



SELECT * FROM plsql!01_product; SELECT * FROM plsql!01_purchase; Результаты его выполнения показаны на рис. 5.23. '• '. '



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



Встроенные функции SQL



161



* Oracle SQL'Plus



ЯНЕЗ



£ile Edit Seatch Oplions Help SQL> /»



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



Snail Widget Medium Uodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar + Square Zinculator



PRODUCT_PRICE QUflHTITV_OH_HflHD Lfl$T_STOC



99 75 50 25 9.95 45



б rows selected.



1 15-JftN-e3 10QO 15-JSN-82 160 1S-JflN-03



ieeee



1234 15-JflN-B4 1 31-DEC-82



....-,,.



SQL> SQL> SELECT » FROM plsq!101_purchase; PRODUCT



NftME



Small Widget Medium Uodget Chrome Phoobar Snail Widget Medium Uodget Round Snaphoo



QUfiHTITV PURCHASE. SflL 1 75 2 8 26 5



14-JUL-B3 СЙ 14-JUL-83 BB 14-JUL-03 Gfi 15-JUL-B3 Gfl 15-JUL-Q3 LB 16-JUL-B3 СЙ



6 rows selected. SQL>



Рис. 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 SELECT SELECT SELECT FROM



COUNT(1) FROM plsqll01_product; COUNT(product_name) FROM plsql!01_product; ' COUNT(last_stock_date) FROM plsqll01_product; COUNT(last_stock_date) / COUNT(product_name) "Populated Records" plsql!01_product;



163



Встроенные функции SQL f, Oracle SQL-Plus File Edit Search flptions Help SQL> 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 plsqllGlJpurchase GROUP BY product_name;



После ввода этой команды сравните полученные результаты с рис. 5.26.



Встроенные функции SQL



165



* Oiacle SQL'Plus



MI-ТЕЗ



File E dit Search Options Help SQL> SELECT « FROM plsq!1B1_purchase; PRODUCT NAME



QUANTITV PURCHASE_ SAL



Small Widget Medium Uodget Chrome Phoobar Small Widget Medium Uodget Round Snaphoo



1 75 2 8 20 5



1J»-JUL-03 Ut-JUL-03 U»-JUL-03 1S-JUL-B3 15-JUL-83 16-JUL-03



СА BB GA GA LB CA



б rows selected. SQL> SQL> SELECT product_nane, SUM(quantity) 2 FROM plsqltei_purchase 3 GROUP BV product_nane; PRODUCT



SUM(QUANTITV)



NftME



2



Chrome Phoobar Medium Uodget Round Snaphoo Small Widget



95 5 9



SQL>



Рис. 5.25. Использование конструкции GROUP BY



••••' • •< esc



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 purchase 8 GROUP BV pruduct_nane; Product Chrome Phoobar Medium Uodget Round Snaphoo Small Widget



Total Sold 2 95 5 9



Average Transactions 2 47.5 5 4.5



1 2 1 2



4



J .



Fewest



Most



2 2в 5 1



2 75 5



8



SQL>



AJ



Рис. 5.26. Использование нескольких групповых функций в одном операторе SELECT



A



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_purchase GROUP 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_purchase GROUP 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 P r o d u c t ' , 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', 1 'U , '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»



99



1 )+2,



) 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 2. С.



169



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 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 CBB', 'Bobby", 'Barkenhagen', '28-FEB-02'); INSERT INTO plsql!01_person VALUES CLB', '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 1 ('Chrome Phoobar , 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 1 ('Small Widget , 'CA', 44-JUL-03', 1); INSERT INTO plsqllOl purchase VALUES ('Medium Wodget', 'BB', '14-JUL-03', 75); INSERT INTO plsql!01_purchase VALUES 1 ('Chrome Phoobar', 'GA', '14-JUL-03 , 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-1011, '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);



Индексы



. •• •
flLTER TflBLE plsqll81_person MODIFY (first_nane HOT HULL);



183



ИЕЕЗ



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 Help SQL> ALTER TABLE plsq!101_person 2 ADD CONSTRAINT plsql101_person_unlque UNIQUE (



3 4 5 б 7



first_nane, last name. hire~date



Table altered. SQL>



Рис. 6.7. Создание ограничения уникальности ИНЕЗ



Ж Oiitcle SQL-Plus



i



£Je £dit Search Qptions Ц* SQL> INSERT INTO plsqll01_person UALUES ( 2 ' L N ' , 'Linda 1 , •Norton*. •B1-JUN-B31);



'



1 row created. SQL> SQL> INSERT INTO plsqll81 person UALUES ( 2 -NL 1 . -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 считывает все существующие записи таблицы и представляет их в индексе. По этой причине создание ограничения уникальности будет успешным только в том случае, если оно не нарушается текущими записями таблицы. Если таблица содержит записи с повторяющимися значениями в указанных вами столбцах, то в ответ на команду ALTER TABLE...ADD CONSTRAINT будет выдано сообщение об ошибке с объяснением проблемы.



CHECK



Контрольное ограничение (check constraint) позволяет определить, каким условиям должны удовлетворять входные данные, чтобы Oracle разрешил их ввод. Контрольное ограничение можно определять для каждого столбца таблицы. Например, вы можете объявить, что значения в столбце с ценами должны быть положительными числами, и одновременно потребовать, чтобы значения в столбце с датами лежали в определенном диапазоне. Контрольные ограничения входят в число наиболее мощных инструментов, обеспечивающих чистоту хранимых данных. Команда создания контрольного ограничения на столбец существующей таблицы имеет следующий синтаксис: ALTER TABLE имя_таблицы ADD CONSTRAINT [имя_ограничения] СНЕСК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 (



2 3 Ч



5 6 7 8



Table



CONSTRAINT reasonable_date CHECK( purchase_date IS NOT NOLL AND TO_CHflR(purchase_date, •VYVV-MM-DD 1 ) >- '20вв-06-ЗВ'



altered.



SQL> SQL> INSERT INTO plsql1B1 purchase UALOES ( 2 'Small Midget 1 . 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







Глава 6



188 AND



TO_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



3 ;



'Spring-Loaded Pit Puller 1 , 49, 5, '30-DEC-01')



'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 Snaphoo 1 , 1.95, 10, '30-DEC-01')



ALTER TABLE plsql!01_product DISABLE



CONSTRAINT reasonable_stock_date;



INSERT INTO plsql!01_product VALUES ( 'Red Snaphoo', 1.95, 10, ' 3 0 - D E C - 0 1 ' )



Индексы и ограничения



191



ALTER TABLE plsql!01_product ENABLE CONSTRAINT reasonable 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 Help SQL> IHSERT INTO plsqll01 product UflLUES ( 1 1 2 'Red Snaphoo , T.95, 10, '30-DEC-01 ) 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-01 1 )



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 ( 1 1 2 'Blue Snaphoo . 1.95, 1B. '3B-DEC-B1 ) 3 ;



-,'



1 row created.



.



.



• ' , ' ; , -



'



















"







'



j -



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 PRODUCT_NAME PRODUCT PRICE QUANTITY ON HAND LAST STOCK DATE



PLSQL101 PERSON PERSON_CODE RRST NAME LAST NAME HIRE DATE



PLSQL101 PURCHASE PRODUCT NAME SALESPERSON PURCHASE DATE QUANTITY



Рис. 6.14. Связи таблиц PL/SQL 101 (стандарт IDEF1X)



Глава6



196



является 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 PRODUCT NAME PRODUCT PRICE QUANTITY ON HAND LAST STOCK DATE



PLSQL101 .PERSON PERSON CODE



FIRST NAME LAST NAME HIRE'DATE



II



AA



PLSQL101 PURCHASE PRODUCT NAME SALESPERSON PURCHASE DATE QUANTITY Рис. 6.15. Связи таблиц PL/SQL 101 (стандарт IE)



Индексы и ограничения



197



Можно привести много повседневных примеров первичных ключей. Зайдите в магазин (или позвоните в местную службу доставки пиццы), и если вы уже зарегистрированы у них как постоянный клиент, у вас, вероятно, спросят телефон, чтобы найти соответствующую учетную запись. В этой базе данных первичным ключом Служит телефонный номер клиента. В учебных заведениях студентам присваивается идентификационный номер, работодатели присваивают своим работникам номер служащего, а в каталогах для заказа по почте проставляются номера товаров. Все эти значения — первичные ключи соответствующих таблиц. Чтобы использовать первичный ключ главной таблицы для связывания таблиц, необходимо поместить ссылку на него в подчиненную таблицу. Это осуществляется путем включения в подчиненную таблицу столбца (или группы столбцов), имеющего точно такой же тип данных, как и у первичного ключа главной таблицы. Когда вам нужно создать в подчиненной таблице запись, ссылающуюся на запись главной таблицы, вы включаете в эту запись значение^) первичного ключа. Например, чтобы определить, какой товар был куплен в результате транзакции, необходимо включить в запись о транзакции название или номер товара, в зависимости от того, что используется в качестве первичного ключа главной таблицы. Столбцы подчиненной таблицы, содержащие значения первичного ключа из главной таблицы, называются внешними ключами (foreign keys). Внешний ключ позволяет подчиненной записи ссылаться на главную запись. Например, в таблице PLSQL101_PURCHASE столбец названий товаров является внешним ключом по отношению к таблице PLSQL101_PRODUCT. Подобно этому, столбец продавцов в PLSQL101_PURCHASE является внешним ключом по отношению к таблице PLSQL101_PURCHASE. На приведенной ниже иллюстрации показана модель данных тестовых таблиц, где внешние ключи обозначены как "(FK)". PLSQL101 PRODUCT PRODUCT NAME PRODUCT PRICE QUANTITY ON HAND LAST STOCK DATE



PLSQL101 PERSON PERSON.CODE FIRST NAME LAST NAME HIRE DATE



.



*'•*. PLSQL101 PURCHASE PRODUCT NAME (FK) SALESPERSON (FK) PURCHASE DATE QUANTITY



Глава 6



198



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



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



Команда, позволяющая определить, какие столбцы существующей таблицы будут использоваться в качестве первичного ключа, имеет следующий синтаксис: ALTER TABLE имя_таблщы ADD PRIMARY KEY (имя_&иолбца_1, имя_столбца_2,...);



j|| Примечание



> «*"•"



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



Чтобы создать первичный ключ по столбцу PLSQL101_PRODUCT, введите следующую команду и сравните результаты с показанными на рис. 6.16: ALTER TABLE plsql!01_product ADD 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 Byproduct 2 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_purchase ORDER BY product_name;



Каквидите, таблица РЬ80Ь101_РиК.СНА8Есодержитзаписьо транзакции для "Round Snaphoo", хотя такого товара нет в таблице PLSQL101_PRODUCT. Прежде чем вы сможете создать ограничение внешнего ключа для таблицы покупок, вам потребуется изменить название товара. Однако сначала попробуйте создать ограничение, ничего не меняя, чтобы увидеть реакцию Oracle. Команда создания ограничения внешнего ключа имеет следующий синтаксис: ALTER TABLE имя_подчиненной_таблицы ADD CONSTRAINT имя_ограничения FOREIGN KEY (имена_столбцов_подчиненной_таблицы) REFERENCES имя_главной_таблицы



200



Главаб



.* OiacleSUL'Plus file £dil Search flptions Help SQL> SELECT product_name 2 FROM plsq!1B1_product



НИ S3



3 ORDER BV product_name; PRODUCT



NftME



Anodized FraniFier Blue Snaphoo Chrome Phoobar Extra Huge Hega Phoobar + Mediun Uodget Red Snaphoo Round Chrome Snaphoo Small Widget Square Zinculator 9 rows selected. SQL> SQL> SELECT DISTINCT product name 2 FROM plsql101_purchase 3 ORDER BV product_name; PRODUCT_NAME



Chrome Phoobar Medium Uodget Round Snaphoo Snail Widget SQL>



Рис. 6.17. Ручной поиск подчиненной записи, которой не сопоставлена главная запись Применение этого синтаксиса к таблицам товаров и покупок даст нам следующую команду: ALTER TABLE plsql!01_purchase ADD CONSTRAINT plsql!01_purchase_fk_product FOREIGN KEY (product_name) REFERENCES plsqll01_product r



Введите ее, и вы увидите результаты, показанные на рис. 6.18. Фраза "parent key not found" в сообщении об ошибке говорит о том, что подчиненная таблица содержит одно или несколько названий товаров, отсутствующих в главной таблице. Прежде чем ограничение заработает, необходимо обновить некорректное название товара в подчиненной таблице. Чтобы сделать это и повторить создание ограничения, введите следующие команды:



Индексы и ограничения



201



ЯНЕЗ



* Oiacle SOL'Plus



file £dir S»«ch Option», HetfS«;,'



ц



SQL> ALTER TABLE plsqll61 purchase



2 ADD CONSTRAINT plsqlToi_purchase_fk_prodiict 3 FOREIGN KEV (product папе) 4 REFERENCES plsqll81 product; ALTER TABLE plsqllB1_purchase



-J :



.



*



ERROR at line 1:



ORA-02298: cannot enable (PLSQL181.PLSQL101_PURCHASE_FK_PRODUCT) - parent key



not 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', 'Cfl 1 , •17-JUL-831. 1)



ERROR at line 3: ORfl-02291: integrity constraint



Рис. 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 , 2 plsqll fl1_per son. las t_nane, — 3 plsql101_person.first_nane, 4 plsql101 purchase. quantity 5 FROM plsqll 01_purchase, 6 plsqll 01_person 7 WHERE plsqll B1_person.person_code - plsql101_purchase. salesperson



низойти :



'



.d J •f'-



8 ; PRODUCT_HAME



LAST_NAME



FIRST_NAME



Small Widget Round Chrome Snaphoo Small Widget Chrome Phoobar Small Widget Medium Wodget Medium Wodget



Atlas Atlas Atlas Anderson Anderson Barkenhagen Baxter



Charlene Charlene Charlene Gary Gary Bobby Laren



7 rows selected.



l



QUANTITY 1 5 1 Z в 75 20 •"•'' - '"'



SQL> |



у



'



•' •"•''











• ;-' ' •'•



"•; >



Рис. 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 FROM plsql!01_product, plsql!01_person, plsqll01_purchase WHERE plsqll01_product.product_name = plsql!01_purchase.product_name and plsql!01_person.person_code = plsql!01_purchase.salesperson



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



205



Индексы и ограничения



тшщ



J-* tlrocle SQL'Plus



£!e £dit Seech fipdoru Help SQL> SELECT plsql101_purchase.product_name,



2



3



k



5



6



л.



plsql101_person.last_nari)e.







plsql1B1_person.First_name,



FROM



i 7 ;



plsql101~purchase. quantity plsql101_purchase,



plsq!101 person



PRODUCT_NAME



LflSTJMME



FIRST_NflME



Small Widget Medium Wodget Chrone Phoobar Small Widget



Atlas Atlas Atlas Atlas Atlas Atlas Atlas Anderson Anderson Anderson Anderson Anderson Anderson Anderson Barkenhagen Barkenhagen Barkenhagen Barkenhagen Barkenhagen Barkenhagen Barkenhagen Baxter Baxter Baxter



Charlene Charlene Charlene Charlene Charlene Charlene Charlene Garu Garu Gary Gary Gary



HediuR Wodget



Round Chrome Snaphoo Small Widget Small Widget Medium Wodget



Chrone Phoobar Small Widget Medium Wodget



Round Chrone Snaphoo



Small Widget Small Widget Medium Wodget Chrone Phoobar Small Widget Medium Wodget Round Chrone Snaphoo Small Widget Small Widget Medium Wodget Chrome Phoobar



Small Widget



Medium Wodget Round Chrome Snaphoo Small Widget



Small Widget Medium Wodget Chrome Phoobar Small Widget Medium Wodget Round Chrome Snaphoo Snail Widget



i



•^



Baxter



Baxter Baxter Baxter Morton



Norton Norton



Norton Morton Morton Norton



QUflNTITV



1



75



• , . 2; .;: •



:



20 5 1



Л , 75 2



8 21 5 1' 1



Gary



Gary



Bobby Bobby Bobby Bobby Bobby Bobby Bobby



75



2 8 20 5 1 1 75 2 8 20 5 1 1 75 2 8 20 5 1



Laren Laren Laren Laren Laren Laren Laren Linda



Linda Linda Linda Linda Linda Linda



., -



35 rows selected. SQL>



\



.iLJ



Рис. 6.21. Создание соединения без конструкции WHERE



,



jf*



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_name and b.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_name and 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 pure WHERE 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



Глава 6



208



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



Операторы соединения Теперь, когда вы научились выполнять традиционные соединения таблиц, пора познакомиться с некоторыми альтернативными типами соединений. Они применяются в разных ситуациях — например, когда вам нужно комбинировать содержимое многих таблиц, имеющих похожие структуры, или сравнивать записи из разных таблиц, чтобы узнать, какие записи присутствуют в обеих таблицах или только в одной из них. Все это можно делать с помощью операторов соединения (join operators), соединяя ими два оператора SELECT. Существует четыре различных оператора соединения; они описаны в таблице 6.3. На приведенной ниже иллюстрации графически показано, что получается в результате их применения.



Таблица 1 UNION



UNION AT All



Таблица 2



Таблица 6.3. Операторы соединения. Оператор соединения



Получаемые результаты .



UNION



Все строки из обоих операторов SELECT; повторяющиеся значения удаляются



UNION ALL



Все строки из обоих операторов SELECT; повторяющиеся значения показываются



INTERSECT



Строки, которые возвращены и первым, и вторым оператором SELECT



MINUS



Строки, которые возвращены первым оператором 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 UNION SELECT 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*



Глава6



210 * Otacle SQL-Plus £te £dit Search flptions H SELECT product_name F R O M 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> 2 3 4



» SELECT product_nane FROM plsq!101_purchase UNION SELECT product_nane FROM plsql181_purchase_archiue 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_purchase UNION ALL SELECT product_name FROM plsql!01_purchase_archive ORDER 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 была перехвачена где-то по дороге, а вместо нее пришли дешевые подделки. Вы хотите узнать, что еще должно было прийти в тот день, чтобы проверить и эти товары. Вы не знаете, когда планировалось поступление Small Widget, и поэтому не можете явно указать эту дату в конструкции WHERE. Однако можно запросить дату у Oracle и использовать ее так, как если бы она была введена вручную. Для этого вы должны указать в конструкции WHERE опера- . тора SELECT, что необходимо получить дату последнего пополнения запасов Small Widget, а затем использовать ее для фильтрации записей о других товарах. Введите следующую команду и сравните результаты с теми, что показаны на рис. 6.23: SELECT * FROM plsqllOl_J>roduct



WHERE last_stock_date = ( SELECT last_stock_date FROM



plsg!101_product



WHERE product_name = 'Small Widget'



Чтобы лучше освоить эту технику, создайте новый оператор SELECT, возвращающий все записи о товарах с ценой, равной цене Red Snaphoo.



£ite £d» Search flptiohs SQL> SELECT "•'



2 FROM 3 WHERE 4 5 6



7 e ;



H*



plsql1B1 product last_stock date - ( SELECT last stock date FROM plsqltBI product WHERE product name - 'Snail Widget•



)



PRODUCT_NAHE



PRODUCT_PR1CE QUflNTITV_ON_HflND LfiST_STOC



Snail Widget



99



1 15-JAH-03



Chrone Phoobar



SB



100 15-JftN-B3



SQL>



Рис. 6.23. Подзапрос, основанный на дате последнего пополнения запасов



Индексы и ограничения



213



Ваши результаты должны совпадать с показанными на рис. 6.24. Вы должны не просто подставить значение 1.95 в конструкцию WHERE; пусть оператор сам определит, какова цена Red Snaphoo. Ключевой характеристикой только что написанных подзапросов является то, что они возвращают единственное значение. Например, когда вы пишете подзапрос для определения даты последнего пополнения запасов Small Widget, может быть возвращено только одно значение, поскольку для хранения этой даты в таблице товаров отведен только один столбец. В этом случае в конструкции WHERE родительского оператора можно уверенно ставить знак равенства. (Если бы подзапрос потенциально мог возвращать несколько значений, знак равенства нельзя было бы использовать, поскольку дата последнего пополнения запасов в родительском операторе всегда имеет только одно значение.) Подзапрос такого типа называется однострочным подзапросом (single-row subquery), поскольку он может возвращать только одну строку результатов. (Разумеется , родительский оператор может возвращать любое число строк на основе одного ответа подзапроса.) Подзапрос и родительский оператор могут ссылаться на совершенно разные таблицы. Допустим, вам нужно узнать обо всех продажах, которые выполнил Гари Андерсон. Вы знаете только имя продавца, но не код, под которым он значится в таблице покупок. (Для наших тестовых таблиц, где личный код — это просто инициалы, такая постановка задачи не имеет особого смысла, но в реальной базе данных наверняка использовался бы номер, не имеющий очевидной связи с именем, и догадаться, кого он представляет, было бы непросто.) Следовательно, нужно сделать так, чтобы оператор 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*'':•'



НИИ :



; ;



:



d PRODUCT_HflME Red Snaphoo Blue Snaphoo



PRODUCT_PRICE QUflHTITV_ON_HflHO LftST_STOC 1.95 1.95



10 31-DEC-01 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_product WHERE 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



ига is



* Oiacle SQf Plus File Edit Jearch Options Help SQL> SELECT * 2 FROM plsql1B1_purchase 3 ORDER BV pi-oduct_name; PRODUCT_NflHE



SflL PURCHflSE_



Chrome Phoobar Medium Uodget Mediun Uodget Round Chrome Snaphoo Small Widget Small Widget Snail Widget



Си BB LB СЙ СЙ Си Си



14-JUL-83 14-JUL-B3 15-JUL-B3 16-JUL-B3 14-JUL-83 15-JUL-03 17-JUL-03



QUflNTITV



2 75 20 S 1 8 1



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 '• PRODUCT_NfiM£ flnodized FramiFier Blue Snaphoo Extra Huge Mega Phoobar * Red Snaphoo Square Zinculator



:,.



. '•



PRODUCT_PRICE qUflNTITV_DH_HflNO LflST_STOC



49 1.95 9.95 1.95 45



10 1234 10 1



3B-DEC-01 l5-JflN-0»i 31-OEC-01 31-OEC-82



SQL>



Рис. 6.25. Использование многострочного подзапроса для поиска несовпадающих записей Как видите, в результате выполнения этой команды были изменены цены только тех товаров, которые ни разу не были проданы. Удобно, не правда ли?



Подзапросы, возвращающие более одного столбца Все подзапросы, которые вы видели до сих пор, извлекали только один столбец данных. Однако они могут возвращать и несколько столбцов. Эту возможность демонстрирует приведенный ниже код, который анализирует названия товаров и даты покупок из таблицы PLSQL10 1_PURCHASE, чтобы вернуть записи только о самых последних покупках каждого товара. (Проводя собеседование с людьми, работа которых будет связана с PL/SQL, я задаю ряд тестовых вопросов. Один из них требует знания описанной техники; очень немногие отвечают правильно.)



216



Глава 6



SELECT * FROM plsql!01_purchase ORDER BY product_name, purchase_date; SELECT FROM WHERE IN



* plsqllOi_purchase (pro'duct_name, purchase_date) (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 FROM



.



иия_ таблицы_ 1.имя_ столбца_1, имя таблицы_2.имя_столбца_2 имя_ таблицы_1, имя таблицы_2



г



A. Пять записей из таблицы 1. B. Десять записей из таблицы 2. C. Пятнадцать записей с данными из обеих таблиц. D. Пятьдесят записей с данными из обеих таблиц. E. Количество выведенных записей будет зависеть от того, сколько записей таблицы 1 имеют те же значения, что и записи таблицы 2.



Ответы на вопросы 1. А.



Ускоренное выполнение команд INSERT



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



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



Объяснение Обратитесь к разделу "Как создавать индексы", чтобы освежить в памяти синтаксис команды CREATE INDEX.



Индексы и ограничения 3.В.



219



В*-дерево



Объяснение Составной индекс — это просто индекс по многим столбцам; кардинальность столбцов тут ни при чем. Битовые индексы предназначены для столбцов с низкой кардинальностью, т.е. с малым количеством различных значений. Вариант 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 DROP DROP DROP DROP



TABLE TABLE TABLE TABLE TABLE



plsql!01_purchase; plsql!01_product; plsql!01_person; plsqll01_old_item; 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 laststock_date



223



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 1 ('Small Widget , 99, 1, 45-JAN-03 ' ) ; INSERT INTO plsql!01_product VALUES ('Medium Wodget', 75, 1000, ' 15-JAN-02 ' ) ; INSERT INTO plsql!01_product VALUES 1 ('Chrome Phoobar , 50, 100, ' 15-JAN-03 ' ) ; INSERT INTO plsql!01_product VALUES 1 ('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.', '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 AND TO_CHAR(purchase_date, 'YYYY-MM-DD1) >= '2000-06-30'







224



ALTER TABLE plsql!01_purchase ADD CONSTRAINT plsql!01_purchase_fk_product FOREIGN KEY (product_name) REFERENCES plsql!01_product; ALTER TABLE plsqll01_purchase ADD CONSTRAINT plsql!01_purchase_fk_person FOREIGN KEY (salesperson) REFERENCES plsql!01_person; CREATE INDEX plsql!01_purchase_product ON plsql!01_purchase(product_name); CREATE INDEX plsql!01_purchase_salesperson ON plsql!01_purchase(salesperson); INSERT INTO plsql!01_purchase VALUES 1 ('Small Widget', 'CA', '14-JUL-03 , 1) ; INSERT INTO plsql!01_purchase VALUES 1 1 ('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 1 ('Small Widget , '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_product SET product_price = product_price * .9 WHERE product_name NOT IN ( SELECT DISTINCT product_name FROM plsqllOl purchase



CREATE TABLE plsq!101_old_item ( item_id CHAR (20), item_desc CHAR (25)



INSERT INTO plsq!101_old_item VALUES ('LA-1011, '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 1 CNY-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 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 1 ('Large Harflinger', 'GA', '22-JUN-01 , 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-021, 80); INSERT INTO plsq!101_purchase_archive VALUES ('Chrome Phoobar1, '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



227



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



Оператор 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 pure WHERE prod.product_name = purc.product_name AND



pers.person_code = pure.salesperson



SELECT * FROM plsqll01_purchase_log;



Fie



£dit



Search



2 3 ft 5 6 7 8 9 10 11



Options



Help



12



BHD



13



pers. person code - pure. salesperson



1» 15 ;



^l



SELECT pure. pur chase_date. prod.product_nane. prod.product_price, pure. quantity , pers.First_name, pers. las t_nane FROM plsqllfll .product prod. plsq!101_person pers. plsql101_purchase pure WHERE prod. product name * pure .product_nane











)



7 rows created. SQL> SQL> SELECT » FROM plsq!101_purchase_log; PURCHASED PROOUCT_MAME 1it-JUL-B3 1H-JUL-03 14-JUL-83 15-JUL-83 1S-JUL-03 16-JUL-03 17-JUL-G3



Small Uidget Medium Wodget Chrome Phoobar Snail Widget Medium Wodget Round Chrone Snaphoo Snail Widget



PROOUCT_PRICE 99 75 50 99 75 25 99



quflHTITY S*LES_FIRST_NftM S 1 75 2 8 28 5 1



Charlene Bobby Gary Gary Laren Charlene Charlene



7 rows selected. SQL>



Рис. 7.1. Копирование записей из одной таблицы в другую



Й В и Й В Й и



Глава?



228



Как видите, таблица PLSQL101_PURCHASE_LOG содержит удобные для анализа наборы данных о каждой транзакции из таблицы PLSQL101_PURCHASE.



Создание новой таблицы на основе уже существующей В рассмотренном выше методе копирования данных из одной таблицы в другую предполагалось, что таблица назначения уже существует. Это вполне подходит для ежедневного добавления записей в таблицу назначения, но вместе с тем есть и более простой способ создать эту таблицу. Нужно использовать разновидность команды CREATE TABLE со следующим синтаксисом: CREATE TABLE имя_новой_таблицы AS оператор SELECT i В данном случае оператор SELECT будет тем же самым оператором SELECT, с помощью которого вы заполняли первую таблицу назначения. Введите показанный ниже код, чтобы создать вторую таблицу назначения с использованием описанной техники, и сравните результаты с рис. 7.2. ИИ El



I *. Omcle SOL'Plus



file £dil Se«ch Uptons НФ SQL> CREATE TABLE plsqll 01_purchase_log2 AS 2 SELECT purc.purcbase_date, 3 prod.product_nane, U prod.product_price, 5 pure. quantity, 6 pers.first_nane, 7 pers.last_nane 8 FROM plsqll 01_product prod, 9 plsqll 81 person pers, 11 plsq!101_purchase pure 11 WHERE prod. product name - pure. product nane 12 AND 13 pers.person_code - pure. salesperson



:';'•.•,



.1 —



Table created. SQL> SQL> SELECT « FROM plsqll 01_purchase_log2; PURCHASE. PRODUCT_NAME



14-JUL-B3 Snail Widget 14-JUL-D3 Medium Uodget 14-JUL-03 Chrome Phoobar 15-JUL-83 Snail Widget 15-JUL-83 Medium Wodget 16-JUL-B3 Round Chrome Snaphoo 17-JUL-03 Snail Widget



7 rows selected. SQL> JJ



PRODUCT_PRICE



99 75



51



99 75 25 99



QUANTITY FIRST_NAME



1 charlene



75 Bobby 2 Gary



8 Gary



20 Laren S Gharlene 1 Charlene



L A В



A A В A A



'



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_name AND 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_log ADD data_load_date VARCHAR2(8); DESC plsql!01_log



Глава?



230







• .* Oracle SQL-Plus File Edk Search Qptions tidp SQL> DESC plsqUei log Name



Hlil 13



Null?



Type







J



DATE UARCHAR2(25) NUMBER (it, 2) NUMBER(it,2) UARCHAR2(15) UARCHAR2(2Q)



PURCHASE DATE PRODUCT NAME PRODUCT PRICE qUANTITV FIRST NAME LftST_NAME SQL> SQL> ALTER TABLE plsqlllH log 2 ADD data_load_date UARCHAR2(8) NULL; Table altered. SQL> SQL> DESC plsql101 log Name PURCHASE DATE PRODUCT NAME PRODUCT PRICE qUANTITV FIRST NAME LAST NAME DATA_LOAD_DATE



Null?



Type DATE UARCHftR2(25) NUMBER(lt,2) HUMBER(4,2) UARCHAR2(15) UflRCHftR2(2B) UARCHAR2(8)



SQL>



Рис. 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



231



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



• * Uiacle SQL-Plus File Edit Search Options Help SQL> DESC plsq!101 log Нале



Null?



PURCHASE DATE PRODUCT NAME PRODUCT PRICE QUANTITY FIRST NAME LAST NftME DATA_LOAD_DATE



Type DATE UARCHAR2(25) HBHBER(4.2) NUMBER(ll,2) UARCHAR2(15) UARCHAR2(20) UARCHAR2(8)



SQL> SQL> ALTER TABLE plsql1B1 log 2 MODIFY data_load_date DATE;



'



'



'



.



Table altered.



SQL> SQL> DESC plsql101 log Name



- -



Null?



PURCHASE DATE PRODUCT NAME PRODUCT PRICE QUANTITY FIRST NAME LAST NAME DATA_LOAD_DATE



Type DATE UARCHAR2(25) NUM8ER(l|,2) HUMBER(it,2) UARCHAR2(15) UARCHAR2(20) DATE



SQL>



jJJ



Рис. 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 Help SQL> UPDATE plsql101_log SET data_load_date -



>



15-ОЕС-2вВ3



1



7 rows updated. SQL>



SQL> DESC plsql101 log Name



Null?



DATE



f>URCHASE_DATE PRODUCT_NAME



UARCHAR2(2S)



PRODUCTlPRICE



NUMBER(1»,2) NUMBEHC.,2)



QUANTITY FIRST_HflME LAST_NflME



UARCHAR2(15) UARCHAR2(2D) DATE



DATfl_LOAD_DATE SQL>



SQL> ALTER TABLE plsqliei_log HODIFV data_load_date NOT NULL; Table altered. SQL> SQL> DESC plsql101 log Name PURCHASE_DATE PRODUCT_NAME PRODUCT_PRICE QUANTITY FIRST_NAME LAST_NAME DATfl_LOAD_DATE



Null?



Type



DATE UARCHAR2(2S) NUMBER(4,2) NUMHER(U,2) UARCHAR2(15) UARCHAR2(2e) NOT NULL DATE



SQL> |



Рис. 7.5. Изменение null-опции существующего столбца



Представления Идея представления (view) проста: определить запрос, который предполагается часто использовать, сохранить его в базе данных Oracle и разрешить пользователям обращаться к нему по имени, как к обычной таблице. Когда пользователь выбирает данные из представления, Oracle выполняет соответствующий запрос, организует результаты так, как определено в представлении, и выдает их пользователю. Для пользователя представление выглядит как таблица, из которой поступают данные. Однако на самом деле данные поступают через представление, из одного или нескольких других источников. Зачем нужны представления? По целому ряду причин. В частности, представления широко применяются для соединения данных из двух и более таблиц и выдачи их пользователям в виде одного легко читаемого списка. Упрощая процесс выборки записей до такой степени, когда пользователям не нужно знать, как соединяются таблицы, вы делаете данные доступными для большего числа людей. С помощью представлений удобно поддерживать безопасность, поскольку ни позволяют ограничивать диапазон строк и столбцов, возвращаемых поль^вателям. Если вы не хотите, чтобы пользователи видели столбец с зарплатой



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



233



из таблицы личных данных, просто не включайте его в определение представления. Для пользователей представления этот столбец не будет существовать. То же самое справедливо и для строк: включите в представление конструкцию WHERE, и возвращаемые записи будут отфильтрованы любым нужным вам образом. Наконец, представления могут сделать работу с таблицами более удобной. Конечно, вы никогда не станете разрабатывать таблицы, столбцы которых имеют непонятные имена или расположены в странном порядке, но вместо вас это могут сделать другие, и рано или поздно вам придется использовать их таблицы. Поскольку представление — это просто хранимый запрос, вам предоставлена возможность менять как имена столбцов, так и порядок их отображения. Например, недавно передо мной стояла задача анализа существующей базы данных, в которой были сотни столбцов с именами типа ID101, ID205, ID3322 и т.д. Мне дали справочник, в котором объяснялось, что содержит каждый из столбцов, но постоянно обращаться к нему было бы слишком нерационально, и к тому же следовало подумать о пользователях, не имеющих справочника. Для каждой таблицы базы данных я создал представление, в котором столбцам присваивались понятные имена. В результате никто больше не пытался извлекать данные непосредственно из таблиц; все пользовались представлениями, так как теперь имена столбцов говорили сами за себя.



Создание представления Метод создания представления — это сама простота. Нужно указать только имя представления и оператор SELECT, который будет выполняться при обращении к представлению. Вот соответствующий синтаксис: CREATE OR REPLACE VIEW имя_представления AS оператор SELECT j Обратите внимание, что здесь присутствует новый элемент: OR REPLACE. Он позволяет создавать новое представление даже тогда, когда представление с указанным именем уже существует. (Разумеется, существующее представление при этом перезаписывается.) Чтобы увидеть, как работает представление, введите следующие команды и сравните результаты с показанными на рис. 7.6: SELECT * FROM plsql!01_purchase; CREATE SELECT FROM WHEJ^E



OR REPLACE VIEW plsql!01_sales_by_atlas_v AS * plsql!01_purchase salesperson = 'CA'



SELECT * FROM plsqllOl_ sales_ by j_ atlas_v;



Глава 7



234



Eh £* Search Option» Help SQL> SELECT » FROM plsql1U1_purchasf> ;



.



_i



PRODUCTJMHE



SALESPERSON



PURCHASE_



Snail Widget Hediun Wodget Chrome Phoobar Snail Widget Hediun Wodget Round Chrome Snaphoo Snail Widget



CA OB GA GA LB CA CA



1*-JUL-83 1"1-JUL-03 14-JUL-83 1S-JUL-03 15-JUL-83 16-JUL-03 17-JUL-03



QUANTITV



1 75 2 8 20 5 1



7 rows selected. SQL> SQL> 2 Э u 5



CREATE SELECT FROM WHERE ;



OR REPLACE VIEW plsqll 01 sales by atlas и AS * plsqll 01_purchase salesperson - *CA'



Uien created. SQL> SQL> SELECT » FRON plsqll 01_sales_by_atlas_u; PRODUCTJMHE



SALESPERSON



PURCHASE_



Snail Widget Round Chrone Snaphoo Snail Widget



CA CA CA



11I-JUL-03 16-JUL-03 17-JUL-03



QUANTITV 1 5 : . 1 ;'i



SOL> ' . :



Л Рис. 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 WHERE pers.person_code = pure.salesperson (+)



;



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 .



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 be i SQL> f



Рис. 7.9. Изменение существующей последовательности Зачем нужно создавать синоним для какого-либо объекта? Главным образом для удобства: если вы часто ссылаетесь на таблицу с длинным именем, то по достоинству оцените возможность использования короткого имени без переименования таблицы и изменения кода, который на нее ссылается. Удобство синонимов проявляется и в том, что они могут облегчить доступ к вашим данным для других людей. Таблицы организуются по идентификатору пользователя Oracle, который их создает, поэтому если другой пользователь захочет обращаться к таблице, созданной вами, то в общем случае ему придется помещать перед именем таблицы ваше имя пользователя, как показано ниже: SELECT * РКОМваше_имя_пользователя.имя_вашей_табл1щы;



241



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



Это может оказаться утомительным занятием, а если вы передадите свою таблицу кому-то другому, то вдобавок потребуется менять весь код, который на нее ссылается. Синонимы позволяют сделать таблицу "видимой" для всех, даже если не указано имя ее владельца. Благодаря этому можно писать 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 Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar * Square Zinculator Anodized Franifier Red Snaphoo Blue Snaphoo



99 75 51 25 1.96 41.5



1.76 1.76



15-JAN-03 15-JAH-02 110 15-JAN-B3 1MH 1234 15-JAH-04 1 31-DEC-02 5 11 31-DEC-B1 II 38-DEC-01



9 rows selected. SQL>



Л



oLJ



Рис. 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 существует так много различных представлений словаря данных, что об их использовании можно написать отдельную книгу (во многих книгах,



243



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



mmm\



Jt Oiacle SQL'Plus File



Edit Search



Options Help



USER_REPPRIORITV_GROUP USER_REPPROP USER_REPRESuLUTION USER_REPRESOLUTION_METHOD USER_REPRESOLUTION STflTISTICS USER_REPRESOL_STATS_CONTROL USER_REPSCHEMA USER_REPSITES USER_RESOURCE_LIMITS USER_ROLE_PRIUS USER_SEGMENTS USEH_SEQUEHCES USER_SNflPSHOTS USER_SNflPSHOT_LOGS USER_SHftPSHOT_REFRESH_TIMES USER_SOURCE USER_SYNONYMS USER_SVS_PRIUS USER_TftBLES USERJTABLESPACES USER_TAB_COLUNNS USER_TAB_COL_STATISTICS USER_TBB_COHMEHTS USER_TfiB_HISTBGRflMS USER_TflB_PRIUS USEB_rBB_PRIUS_MADE USER_TflB_PBIUS_RECD USERJTRIGGERS USER_TRIGGER_COLS USER_TS_QUOTflS USER_TVPES USER_TVPE_flTTRS USER_TVPE_METHODS USER_UPDATABLE_COLUI1NS USERJJSERS USERJJIEWS flUOIT_flCTIONS COLUMN_PRIUILEGES DICTIONflRV DICT_COLUMNS GLOBAL NAME INDEXjUSTOGHIW INDEX_STATS HLS_DATABflSE_PnRflMETERS



NLS_INSTANCE_PARAMETERS HLS_SESSION_PARAMETERS RESOURCE_COST ROLE_ROLE_PRIUS ROLE_SYS PRIUS RDLE_TAB~PRIUS SESSION_PRIUS SESS10N_ROLES TABLE PRIUILEGES



Information about user's priority groups Propagation information about the current use Description of all conflict resolutions for u All conflict resolution methods accessible to Statistics for conflict resolutions for user" Information about statistics collection for с N-way replication information about the curre K-uay replication information about the curre Display resource limit of the user Roles granted to current user Storage allocated for all database segments Description of the user's own SEQUENCES Snapshots the user can look at All snapshot logs owned by the user Snapshots and their last refresh tines for ea Source of stored objects accessible to the us The user's priuate synonyms System privileges granted to current user Description of the user's own relational tabl Description of accessible tablespaces Columns of user's tables, views and clusters Columns of user's tables, uiews and clusters Comments on the tables and uiews owned by the Histograms on columns of user's tables Grants on objects for which the user is the о All grants on objects owned by the user Grants on objects for which the user is the g Triggers owned by the user Column usage in user's triggers Tablespace quotas for the user Description of the user's own types Description of attributes of the user's own t Description of methods of the user's own type Description of updatable columns Information about the current user Description of the user's own uiews Description table for audit trail action type Grants on columns for which the user is the g Description of data dictionary tables and uie Description of columns in data dictionary tab global database name statistics on keys with repeat count statistics on the b-tree Permanent NLS parameters of the database NLS parameters of the instance NLS parameters of the user session Cost for each resource Roles which are granted to roles System privileges granted to roles Table privileges granted to roles Privileges which the user currently has set Roles which the user currently has enabled. Grants on objects for which the user is the g



286 rows selected. SQL>



Рис. 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, обеспечивающем генерацию последовательных номеров для использования в качестве идентификаторов записей и в других операциях подсчета. Команда CREATE SEQUENCE позволяет указать такие параметры последовательности, как начальное, наименьшее и наибольшее значение, величину инкремента, а также разрешить или запретить циклический повтор по достижении граничного значения. Значения последовательности можно включать в оператор 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 AS SELECT * FROM product; D. INSERT INTO product_archive (SELECT * FROM product); 2. Какая из следующих команд переименует таблицу?



' i . •



•'.".;



\ •!.'•••



A. RENAME имя_таблицы новое_имя_таблицы; B. RENAME имя_таблицы ТО новое_имя_таблицы; C. RENAME TABLE имя_таблицы новое_имя_таблицы; D. RENAME TABLE имя_таблицы ТО новое_имя_таблицы\ 3. Какая из следующих команд добавит обязательный текстовый столбец NEW_COLUMN к таблице ТАВ1? A. ALTER TABLE tab 1 ADD new_column VARCHAR2(10) NOT NULL; B. ADD new_column VARCHAR2(10) NOT NULL TO tabl; C. ALTER tabl ADD 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 1 ADD new_column VARCHAR2(10) NOT NULL;



Объяснение Обратитесь к разделу "Добавление столбцов", чтобы освежить в памяти синтаксис команды ALTER TABLE. 4. D. Ускорение доступа к данным за счет прямого обращения к необходимым столбцам Объяснение В варианте D говорится о выигрыше, который дают индексы, а не представления. Использование представления не оказывает существенного влияния на скорость доступа к данным.



Другие полезные средства Oracle 5. В, С.



247



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 DROP DROP DROP DROP



TABLE TABLE TABLE TABLE TABLE



plsqllOljourchase; plsql!01_product; plsqllOljoerson; plsqll01_old_item; 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 1 ('GA', 'Gary , 'Anderson', 45-FEB-02.') ; INSERT INTO plsql!01_person VALUES 1 1 CBB', 'Bobby', 'Barkenhagen , '28-FEB-02 ); INSERT INTO plsql!01_person VALUES 1 ('LB', 'Laren', 'Baxter , '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 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 Phoobar1, 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 NULL AND 1 TO_CHAR(purchase_date, ' YYYY-MM-DD ) >- '2000-06-30'



ALTER TABLE plsql!01_purchase ADD 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_product ON 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 ' , 'BB1, '14-JUL-03', 75); INSERT INTO plsql!01_purchase VALUES ('Chrome Phoobar', 'GA', 44-JUL-031, 2); INSERT INTO plsq!101_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 Chrome Snaphoo ' , 'CA', 46-JUL-03', 5) ; INSERT INTO plsql!01_purchase VALUES ( 'Small Widget1, 'CA', '17-JUL-031, 1) UPDATE plsqll.01_product SET product_price = product_price * .9



Введение в PL/SQL



255



WHERE product_name NOT IN ( SELECT DISTINCT product_name FROM plsql!01_purchase



CREATE TABLE plsq!101_old_item ( item_id CHAR(20), item_desc CHAR(25)



INSERT INTO plsql!01_old_item VALUES CLA- 101', 'Can, Small1); INSERT INTO plsq!101_old_item VALUES ('LA- 102', 'Can, Large'); INSERT INTO plsq!101_old_item VALUES ( 'LA-103', 'Bottle, Small'); INSERT INTO plsqll01_old_item VALUES CLA- 104', 'Bottle, Large'); INSERT INTO plsqll01_old_item VALUES CNY- 101', •Box, Small'); INSERT INTO plsq!101_old_item VALUES ( 'NY-102', 'Box, Large'); INSERT INTO plsq!101_old_item VALUES CNY- 103', 'Shipping Carton, Small1); INSERT INTO plsq!101_old_item VALUES ('NY- 104', 'Shipping Carton, Large'); CREATE TABLE plsql!01_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 plsq!101_purchase_archive VALUES ('Large Harflinger', 'GA1, '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', 80); INSERT INTO plsql!01_purchase_archive VALUES ('Chrome Phoobar', 'CA', '25-JUN-02', 2); INSERT INTO plsql!01_purchase_archive VALUES ('Small Widget 1 , 'JT', '26-JUN-02', 50);



Общие сведения о PL/SQL PL/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 sal tax )



IN IN



NUMBER := 7, OUT NUMBER, 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 состоит из четырех секций: секции заголовка (header section), необязательной секции объявлений (declaration section), выполняемой секции (execution section) и необязательной секции исключений (exception section). Анонимный блок (anonumous block) — это блок PL/SQL без секции заголовка, иначе говоря, секции имени, поэтому он и называется анонимным. Анонимные блоки могут выполняться из SQL*Plus и использоваться в функциях, процедурах и триггерах PL/SQL. Вспомните, что сами процедуры, функции и триггеры также состоят из базовых блоков. Это означает, что базовый блок можно помещать в другой базовый блок. Чуть ниже вы узнаете об этом более подробно. По-видимому, лучший способ понять, что представляет собой базовый блок, — это рассмотреть конкретный пример. Сначала введите команду, которая позволит просматривать в SQL*Plus информацию, выводимую программами: set serveroutput on



*•*



Глава 8



260



Теперь введите код анонимного блока и сравните полученные результаты с рис. 8.1. DECLARE Num_a NUMBER := 6; Num_b NUMBER; BEGIN Numjb := 0; Num_a := Num_a / Num_b ; Num_b := 7; dbms_output .put_line ( ' Value of Num_b ' || Num_b) ; EXCEPTION WHEN ZERO_DIVIDE THEN dbms_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 on SQL> DECLflRE 2 nun_a NUMBER := 6;



3 nun b NUMBER; 4 BEGIN 5 nun b 6 nun_a / num_b; 7 nun_b - 7; 8 dbns_output.put_line( ' Ualue of nun_b ' || num_b) ; 9 EXCEPTION 10 WHEN 2ERO_DIUIDE 11 THEN 12 dbms_output.put_line(' _ _ Trying to diuide by zero1); 13 dbns_output.put_line(' Ualue of nun_a nun_a); 14 dbms_output.put_line( ' Ualue of nun_b nun_b); 15 END; 16



Trying to divide by zero Ualue of nun_a б Ualue of nun_b 0 PL/SQL procedure successfully completed. SQL> |



Рис. 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). В хорошо написанных приложениях исключения столь важны, что им посвящен специальный раздел в конце этой главы, где о них рассказано более подробно. А здесь, в качестве введения, рассмотрим структуру секции исключений. EXCEPTION WHEN имя_исключения 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 on EXECUTE my_first_proc;







Глава 8



264



Команда SET SERVEROUTPUT ON позволяет увидеть выходные данные. Команда EXECUTE запускает процедуру на выполнение. Вы также можете вызвать процедуру из анонимного блока, как показано ниже. Сравните полученные результаты с показанными на рис. 8.2. BEGIN



my_first_proc; END;



Вызов процедур и функций Процедура или функция может иметь формальные параметры как со значениями по умолчанию, так и без них. Фактически она может вообще не иметь формальных параметров. В каждом случае способ вызова процедуры или функции будет отличаться. Однако перечисленное ниже справедливо вне зависимости от наличия или отсутствия параметров. *. Oracle SQL'Plus File Edit Search Options Help SQL> set serueroutput on SQL> CREATE PROCEDURE my_first_proc



2 IS 3 greetings UflRCHflR2(20); 4 BEGIN 5 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> BEGIN 2 ny_first_proc;



3 END; >t /



Hello World PL/SQL procedure successfully completed. SQL> JU



Рис. 8.2. Простая PL/SQL-процедура "Hello World"



mmmi J



Введение в 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) IS BEGIN 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; BEGIN dbms_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,



Глава8



268 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) ;



END;



Третий способ изменения или присваивания значений подробно рассмотрен в следующей главе. Вот короткий пример, результаты которого показаны на рис. 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 Ш*оп» IMP SQL> set serueroutpyt on SQL> CREATE PROCEDURE hike_prices (old_price NUHBER,



percentjiike NUMBER :- 5, new_pcice OUT NUMBER)



IS BEGIN



new_price END hike prices;



old_price * old_price • percent__hike / 100;



Procedure created. SQL> DECLARE



2 3



и



5 б



BEGIN



7



8



9 1в 11 END; 12



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 ' 11 hiked_price); hike_prices (old_price •> price_to_hike, neu_price => hiked_price); dtins_output.put_line( 'price_to_hike after hike ' || price_to_hike); dbns_output.put_line{'hiked_price after hike ' Ц hiked_price);



Price before hike 28 hiked_price before hike D price_to hike after hike 20 hiked_price after hike 21 PL/SQL procedure successfully completed. SQL>



Рис. 8.3. Присваивание значений переменным PL/SQL путем подстановки в качестве фактических параметров



269



Введение в PL/SQL • * Oiacle SQL'Plus Efe & Se«ch Qptions Нф SQL> set serveroutput on SQL> DECLARE 2 product quant NUMBER; 3 BEGIN 4 SELECT quantity_on_hand 5 INTO product quant 6 FROM plsqUOl product 7 WHERE product name - 'Small Widget'; 8 9 dons output. put line ('Snail Widget 10 END; 11 / Small Widget 1



НЗЕЭ



г!



• • • « • • • • • ^ • ^ • • • • ^ • Р ^ И Р И И в И в И в И ^ ^ И Н в Р И



!| product_quant);



I



PL/SQL procedure successfully completed. SQL>



A



Рис. 8.4. Присваивание значений переменным PL/SQL с использованием SQL Параметру product_quant присваивается значение, равное количеству Small Widget.



Использование переменных Переменные являются фундаментальными компонентами 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; BEGIN IF (order_amt < large_order_arat AND order_amt >= small_order_amt) THEN RETURN (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



|x



-l]



IS



4 5 6



small_order_amt NUMBER := 400; large_order_amt NUMBER :- 1000; sroall_disct NUMBER := 1; large_disct NUMBER :- 5;



7 8



BEGIN



9 H 11 12 13 1* 15 16 17 18 19



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. SQL> DECLARE



2 3 4 5 б



BEGIN



7 8 9



dbms_output.put_line dbms_output.put_line dbms_output.put_line dbms_output.put_line dbns output.put_line



10 11 12 .13



END; /



Order



20



-



tiny NUMBER := 20; med NUMBER := 600; big NUMBER := 4550; wrong NUMBER := -35;



AND



(' Order (tiny || • (med |j ' (big j| ' (wrong ||



AND Discount '); '|| compute discounts(tiny)) ; || compute_discounts (med)); I I compute discounts (big)); ' || conpute_discounts (wrong));



Discount



0



«ao 6 4550



227.5



-35 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, чтобы увидеть выходные данные.



273



Введение в PL/SQL



File £dit Search Qptions



Help



SQL> set serueroutput on SQL> DECLflRE 2 just_a_nun NUMBER := 1; 3



4



BEGIN



* 5 6 7 8 9 10 11 END; 12 / 1 2 3 ll 5 6 7 8 9 10



I



«just a loop» LOOP dbns_output .put_line( just_a_nun) ; EXIT just a loop WHEN (just_a_num >= 10);



j



just a nun := just a nun + 1; END LOOP; "



: ' - ""



• ' ;



• '•"



;



'-' :'



•'



'



•-



.



•:'••''.. . ,



' ' '



•'



- '



'



•'



,



.



,



Л



•' '



:



• • .







'• '•••".-.•



PL/SQL procedure successfully completed. SQL> JJ



л . . . . . . , ' • . . ....



: ^Гл



Рис. 8.6. Пример простого цикла DECLARE just_a_num NUMBER := 1;; BEGIN







«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 выполняется условие выхода и цикл завершается.



ю*



Глава 8



274



Цикл WHILE Еще одной разновидностью цикла является цикл WHILE. Он хорошо подходит в ситуациях, когда количество итераций заранее неизвестно, и определяется некоторым внешним фактором. Цикл WHILE имеет следующий синтаксис: WHILE условие_выхода LOOP операторы; END LOOP; Чтобы потренироваться в создании цикла WHILE, введите следующий код и сравните результаты с показанными на рис. 8.7: * Oracle SQL'Plus File Edit Search Options Help SQL> set serveroutput on SQL> DECLARE



3



BEGIN



i» 5 6 7 8 9 END; 10 /



just_a_num NUMBER := 1;



WHILE (just_a_num



Рис. 8.7. Пример цикла WHILE



.



Введение в PL/SQL DECLARE



275 -



just_a_num NUMBER := 1; BEGIN WHILE (just_a_num set serueroutput on SQL> BEGIN 2 FOR just_a_nun IN 1..10 3 LOOP 1 5 6 7 §1 ' 2



dbms_output.put line(just_a_num); END LOOP; END; /



3 k S 6 7



8



9 18



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 Medium Uodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar * Square Zinculator flnodized Franifier Red Snaphoo Blue Snaphoo 1 / 9 rows selected.



99 75 SO ZS 8.96 40.5 W.1 1.76 1 .76



SQL> DECLflRE 2 CURSOR product cur IS 3 SELECT » FROM plsqUOl product >t FOR UPDflTE OF product price; 5 BEGIN 6 FOR product rec IN product cur 7 LOOP 8 UPDflTE plsqll Byproduct 9 SET product price - (product rec. product price 10 , WHERE CURRENT OF product cur; 11 END LOOP; 12 END;



13 /



:



,



i :,



«



0.97)



•'•.



PL/SQL procedure successfully completed. SQL> SELECT product name, product price 2 FROM plsq!1B1 product; PROOUCT_NflNE Snail Midget Medium Uodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar * Square Zinculator Anodized Framif ier , Red Snaphoo Blue Snaphoo 1 - '- •9 rows selected.







PRODUCT_PRICE 96.03 72.75 48.5 24.25 8.69 39.29 42.78 1.71 1 .71 • ' •, *•' • •




2 3 4 5 6 7 8 SflL



BB Си Си Си GA GA LB



SELECT



FROM WHERE ORDER BV



tabl.salesperson, tabl. quantity, tab2.product_price plsq!181_purchase tabl, plsq!181_product tab2 tab1.product_nane = tab2.product_naroe salesperson;



QUflNTITV PRODUCT_PRICE



75 1 5 1 8 2 28



72.75 96.83 24.25 96.83 96.03 48.5 72.75



7 rows selected. SQL> EXECUTE do_commissions; BB 5456.25 189.125 CA 313.31 6.2662 GA 865.24 17.3848 LB 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; BEGIN Num_b := 0; Num_a := Num_a / Num_b; Num b := 7; dbms_output.put_line(' Value of Num_b EXCEPTION WHEN ZERO_DIVIDE THEN DECLARE err_num .NUMBER err_msg VARCHAR2(512) BEGtN dbms_output.put_line('ORA Error dbms_.output.put_line('ORA Error dbms_output.put_line(' Value of dbms_output.put_line(' Value of END; END;



' I I Num_b);



:= SQLCODE; := SQLERRM; Number ' || err_nuia ) ; message ' || err_msg) ; Num_a ' || Num_a); Num_b ' || Num_b);



Введение в PL/SQL



285



Таблица 8.2. Системные исключения



Системное исключение



Причина возбуждения



CURSOR.ALREADYOPEN



Попытка открыть уже открытый курсор



DUPVAL.ONJNDEX



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



INVAUD.CURSOR * ' • • ' . '



• '• '• .'•



: •







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



NO DATA FOUND



Попытка выполнить SELECT INTO, когда SELECT возвращает нулевое количество строк (а также другие причины, описание которых выходит за рамки этой книги)



PROGRAM_ERROR



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



STORAGE.ERROR



Программе не хватает системной памяти



TIME_OUT_ON_RESOURCE



Программа слишком долго ожидала доступности некоторого ресурса



TOO_MANY_ROWS



SELECT INTO в PL/SQL вернул более одной строки



VAUUE_ERROR



PL/SQL встретил неправильное преобразование или усечение данных, или неправильное ограничение на данные



ZERO.DMDE



Попытка деления на нуль



OTHERS



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



286



Глава 8



Я Oracle SQL-Plus £ie Edit Search Options Help SQL> set serueroutput on SQL> DECLARE 2 nun a NUMBER :=' 6; 3 num b NUMBER; 4 BEGIN 5 num_d :- 0;



6 7 8 9



d



nun a :* nun a / num b; пив_Ь :» 7; dbns output. put line(' Ualue of num b EXCEPTION



11 11 12 13 11 15 16 17 18 19 ,28 21 END; 22 /



WHEN ZERO D I U I D E THEN DECLARE



BEGIN



END;



|| nun b);



err nun NUMBER ;= SQLCODE; err msg UflRCHHR2(512) := SQLERRM; dbns output. put_line('ORn Error dbms_output.put_line( 'ORft Error dbms output .put line( ' Ualue of dbms output. put line(' Ualue of



Number ' | | err_nun ) ; message ' I I err_«sg); num__a ' || num_a); nun b ' 1 1 num_b) ;



ORA Error Number -1476 ОНИ Error message ORA-Q1b76: diuisor is equal to zero Ualue of nun_a 6 Ualue of num_b 0 PL/SQL procedure successfully completed.



SQL>



Рис. 8.11. Использование SQLCODE и SQLERRM при обработке системных исключений



Исключения, определяемые программистом Одной из удобных возможностей PL/SQL является то, что он позволяет вам определять свои собственные исключения. При возбуждении и обработке они должны именоваться и объявляться аналогично любым другим элементам PL/SQL. Ниже приведен полный пример объявления и определения исключения. Введите этот код и сравните результаты с показанными на рис. 8.12. set serveroutput on DECLARE quantity!'NUMBER := -2; quantity2 NUMBER := 3; total NUMBER := 0; quantity_must_positive EXCEPTION; FUNCTION find_cost (quant NUMBER) .RETURN NUMBER IS i BEGIN IF (quant > 0) THEN "ELSE



END IF;



RETURN (quant * 2 0 ) ; ' RAISE quantity_must_pqsitive;



Введение в PL/SQL



287



END find_cost; BEGIN total := find_cost(quantity2); total := total + find_cost(quantityl); EXCEPTION WHEN quantity_must_positive THEN dbms_output.put_line('Total until now: ' || total); dbms_output.put_line('Tried to use negative quantity '); END; /



Исключение объявляется в секции объявлений. Аналогично любой другой объявленной там переменной, исключение действительно только для данного блока. Поскольку функция find_cost определена внутри того же блока, в ней можно ссылаться на исключение. Если бы она была определена, скажем, как хранимая функция, вы не могли бы использовать в ней имя этого исключения. Вы можете использовать свои собственные исключения для обработки ошибок, которые система не обнаруживает или не считает за ошибки. Напри* Oracle SQL-Plus Fife Edil Search Options Help SQL> set serueroutput on SQL> DECLARE 2 quantityl NUMBER - -2; 3 quantity NUMBER - 3; 4 total NUMBER := В 5 quantity_must_positiue EXCEPTION; 6 FUNCTION find_cost (quant NUMBER) RETURN NUMBER IS 7 BEGIN 8 IF (quant > 0) 9 THEN 10 RETURN(quant * 20); 11 ELSE 12 RAISE quantity nust_positiue;



13 END IF; END find_cost; 14 15 BEGIN 16 total := Find_cost (quantity2); total := total + find_cost(quantitj|1); 17 18 EXCEPTION 19 WHEN quantity_must_positiue 20 THEN 21 dbns_output.put_line('Total until now: ' || total); 22 dbms_output.put_line('Tried to use negatiue quantity '); 23 END; 24



Total 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 или IN OUT, но в функции такие параметры использовать не следует. D. И те, и другие могут использоваться в конструкции WHERE SQL-оператора SELECT. ч



.



' . ' . ' •



.



2. Что будет результатом выполнения следующего кода?



' - • , . - . . - .ь



. '- ;



«outerjblock» DECLARE scope_num NUMBER' := 3; ...• BEGIN DECLARE



BEGIN



scope_num NUMBER : = 6; Num_a NUMBER := outer_block.scope_num; dbms_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 3. А.



291



Выполняется не более одного набора операторов



Объяснение При использовании 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_index ON 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 1 CGA , '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 ('LB', 'Laren', 'Baxter', ' Ol-MAR-02 ') ; INSERT INTO plsqH01_person VALUES ( 1 'LN', 'Linda , '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)



295



296



Глава9



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 AND TO_CHAR(purchase_date, 'YYYY-MM-DD') >= '2000-06-30'



ALTER TABLE plsq!101_purchase ADD CONSTRAINT plsql!01_purchase_fk_product FOREIGN KEY (product_name) REFERENCES plsql!01_product; ALTER TABLE plsql!01_purchase ADD 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_salesperson on 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-031, 75); INSERT INTO plsql!01_purchase VALUES ('Chrome Phoobar1, 'GA1, '14-JUL-031, 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_product SET product_price = product_price * .9 WHERE 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, Large1); INSERT INTO plsq!101_old_item VALUES ('NY-ЮЗ', 'Shipping Carton, Small'); INSERT INTO plsq!101_old_item VALUES ('NY-104', 'Shipping Carton, Large1); — ========================== 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); — ================== Снижение цен DECLARE



(курсор for update)



===



298



Глава 9 CURSOR product_cur IS SELECT * FROM plsqll01_product FOR UPDATE OF product_price;



BEGIN FOR product_rec IN product_cur LOOP



UPDATE plsql!01_product SET 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 ON DECLARE ТУРЕ 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



is



CURSOR history_cur (person varchar2) IS SELECT AVG(tab2.product_price * tabl.quantity) avg_order FROM plsql!01_purchase_archive tabl, plsql!01_product tab2' WHERE tabl.product_name = tab2.product_name GROUP BY tabl.salesperson HAVING 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; BEGIN SELECT 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



.



•, -.t'a&meaKwwmteM-Wtt&f^:-



. .



THEN



303 -^1Ш^л-т1Г1асг^:с-„юма-тпЦ1011аюстг;р.. >



,:^. a_perform.status := 'Exceptions found';: END current_performance;



.



BEGIN FOR person_rec IN person_cur LOOP current_performance(person_rec,.,one_perform); dbms_output.put_line(one_perform.person_code I ! 1 1 II one_perform.person_name I I I ' II , one_perform.current_sales I I I



I



II one_perform.perform_percent | ''II one_perform.status); END LOOP;



END; / SELECT SELECT SELECT SELECT



* * * *



FROM FROM FROM FROM



plsql!01_person; plsqll01_purchase; plsql!01_purchase_archive; 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



Глава 9



304 1



• Я- ni.-irlr- Ч1Л 4'liK



File £dil



Си Gft BB LB LN



Search



НИЕЗ



Options



Help



ntlas 313.31 7.7 й11 fine Anderson 865.24 a Erroneous or no history Barkenhagen 5456.25 В Erroneous or no history Baxter 1U55 0 fill fine Norton 0 0 Current purchases exception



-d



PL/SQL procedure successfully completed. SQL> SELECT * FROM plsql101_person;



••••;..'



PER FIHSTJffiME



LftST_NftME



HIRE_DflTE



Си Charlene GA Gary BB Bobby LB Laren LN Linda



Atlas Anderson Barkenhagen Baxter Norton



01-FEB-02 15-FEB-D2 28-FEB-02 01-HAR-02 01-JUN-03



SQL> SELECT » FROM plsqll B1_pur chase ; PRODUCT_NAHE



SAL PURCHASE.



Snail Widget Medium Wodget Chrome Phoobar Snail Widget Medium Wodget Round Chrome Snaphoo Snail Midget • .. ;. i •' 7 rows selected.



CA BB GA GA LB CA CA



1



;...•• QUANTITY



14-JUL-B3 14-JUL-B3 11-JUL-B3 15-JUL-B3 15-JUL-83 16-JUL-B3 17-JUI-B3 ,



1 75 2 8 28 5 1 . i •'.-.-



• • ' • • '



,



SQL> SELECT » FROM plsqll 81_purchase_archiue; PRODUCT_NAME



SAL PURCHASE.



Round Snaphoo Large Harflinger Medium Wodget Snail Widget Chrome Phoobar Snail Widget



BB GA LB ZZ CA JT



21-JUN-B1 22-JUN-S1 23-JUN-B1 24-JUN-B2 2S-JUN-B2 26-JUN-B2



QUANTITY 18 5B 28 88 2 58



6 rows selected. "/••':



I !"



'



'







.



'.



SQL> SELECT « FROM plsqll 81_product; PRODUCT_NAHE



PRODUCT_PRICE QUANTITV_ON_HAND LAST_STOC



Snail Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar + Square Zinculator Anodized Framifier Red Snaphoo Blue Snaphoo



96. B3 72.75 48.5 24.25 8.69 39.29 42.78 1.71 1.71



1 15-JAN-B3 1B8B 15-JAN-B2 188 1S-JAN-83 108BB 1234 1S-JAN-B4 1 31-DEC-B2 5 18 31 -DEC- 81 18 38-DEC-81



'.'



! ( •



9 rows selected. ,'.



SQL>



JU Рис. 9.1. Пример привязанных, или динамических, типов



N



^



Другие средства 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.



НП-1ЕН1



I £ Oracle SQL'Plus File



Edit



Search



Options



i j



Help



SQL> DECLflRE



2 3 t 5 6



quant NUMBER := 20; BEGIN INSERT INTO plsq!1B1 purchase VALUES (-Medium Wodget 1 , • 'LN1, •18-flUG-021, i. 8 quant); 9 IF (SQUNOTFOUND) 10 THEN 11 dbms output. put line(' Insert error?*'); 12 END IF;



13 END; 1* /



-



PL/SQL procedure successfully completed. SQL> SELECT * FROM plsqll B1_purchase ; PRODUCT НИНЕ Small Widget Medium Wodget Chrome Phoobar Small Widget Medium Wodget Round Chrome Snaphoo Small Widget Medium Wodget



SftL PURCHftSE_



QUftNTITV



Си 14-JUL-03 BB 14-JUL-03 Си 14-JUL-03 Gfl 15-JUL-03 LB 15-JUL-B3 Си 16-JUL-03 СЙ 17-JUL-03 LN 18-ftUG-B2



1 75 2 8 20 S 1 20



^



\ j .



8 rows selected. SQL> | ' • • ! - * •



jJJ



" ! • • - '



.



.



'"' " •



. . . . .



. . . , . . ,



Рис. 9.2. Вставка записи с использованием PL/SQL







Другие средства PL/SQL



307



DECLARE quant NUMBER := 20; BEGIN INSERT INTO plsqll01_purchase VALUES ('Medium Wodget', : 'LN' , 48-AUG-02', quant); IF (SQL%NOTFOUND) THEN dbms_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_product VALUES ('Large H a r f l i n g e r 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 ) IS BEGIN UPDATE plsql!01_product SET 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 ;ra..prod\jct_name' := ''Small Widget' v 'a'.produet_pri6e := '87; a . quantity_on_hand := 31; , a.last_Stock_date := TO_DATE,( i'23 update_prod (a) ;



END; SELECT * FROM plsqll01_product;



Как видите, все довольно просто. При использовании DML-команд SQL в PL/SQL вы должны учитывать все возможные исключения. Это особенно важно при использовании неявных курсоров, поскольку они не дают таких возможностей контроля, как явные курсоры. A Oracle SQL'Plus Be Edit Search Qptions Help SQL> INSERT INTO plsqll81 product 2 VALUES ('Large Harflinger1.;.„.-, 3 21, 4 100, 5 •29-AUG-01 1 );



d



1 row created. SQL> SQL> INSERT INTO plsq!101_product , 2 UflLUES CRound Snaphoo 1 , 3 12,



4



5



144,



. т'21-JUL-OI1)!



1 row created. SQL> SELECT » FROM plsql181_product; PHODUCTJWME



Snail Widget Hediun Wodget ._ ,• : Chrome Phoobar : Round Chrome. Snaphoo Extra Huge Nega Phoobar Square Zinculator Anodized FramiFier Red Snaphoo Blue Snaphoo Large HarFlinger Round Snaphoo



PRODUCT_PRICE QUANTITV_ONJWND LAST^STOC



96.03 72.75 .48 .5 24.25 8.69 39.29 42.78 1.71 1.71 21 12



1 15-JAN-03 1000 15-JAN-02 100 15-JAN-03 10000 1234 1S-JAN-04 1 31-DEC-02 5 10 31-DEC-01 10 3Q-DEC-01 100 29-AUG-01 144 21-JUL-01



1l""rp'iis selected. SQL>



Рис. 9.3. Новые строки в таблице PLSQL101_PRODUGT



309



Другие средства PL/SQL Jf * Oiacle SQL-Plus file Edit Search Qpliohs Help SQL> CREATE OR REPLACE PROCEDURE update prod



2 3 4



5 6 7



. . 8. 9 10 11



I 12



prod rec plsql101 product%ROWTVPE ) IS



НВЕЭЦД



(



л —



BEGIN



UPDATE plsq!181_product SET last_stock_date » prod_rec.last_stock date, quantity_on_hand - quantity_on_hand . ' ' • ' + prod_rec.quantity on_ham| WHERE product_name - prod_rec.product_name; END update prod;



. .



•; -



/







i Procedure created.







SQL> DECLARE



2 3



a plsq!181 product%ROWTVPE; ^ '



BEGIN



'



'



4 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.



:



- '



'



'•' " t Й :'



I -



SQL> SELECT « FROM plsq!101_product; PRODUCT_NAME



PRODUCT_PRICE QUANTITV_ON_HAND LAST_STOC



Snail Widget Medium Wodget Chrome Phoobar Round Chrome Snaphoo Extra Huge Mega Phoobar + Square Zinculator Anodized Framifier Red Snaphoo Blue Snaphoo Large Harflinger ! Round Snaphoo



J



!••







', .







/'



96.03 72.75 48.5 24.25 8.69 39.29 42.78 1.71 1.71 21 12



•• !



32 1000 100 18000 1234 1



23-NOU-01 15- JAN- 82 15-JAN-03 15-JAN-04 31 -DEC- 02



5 10 10 100 144



31-DEC-01 30-DEC-01 29-AUG-01 21-JUL-01



11 rows selected. SQL>



Глава9 _



-t Oracle SQL-Plus File Edit Search Options Help 'SqL> Set serveroutput oii S(JL> i SflL> BEGIN f ;; , .2.; DELETE FROM plsqliei_product '3 WHERE product_name = 'junk'; 4 IF (SQUHOTFOUND) 5 THEN 6 dbns_output.put_line('No such product'); 7 END IF; S END;



c..-fi / ;v 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 > 5000ORDER BY c3;



Использование команды TIMING для счета реального времени Когда вам нужно произвести более точные измерения, можно воспользоваться командой TIMING. Чтобы показать, как это делается, слегка модифицируем последний пример. Результаты показаны на рис. 9.8. Истекшее время представлено здесь числом 7470. Это означает, что между командами TIMING START и TIMING STOP прошло 7.47 секунды. TIMING START; EXECUTE test_time; COMMIT; TIMING STOP;



Другие средства PL/SQL



3*3



4" Otacle SOL'Plus file Edit £eaich Qptions Table created.



KWI3



SQL> CREATE OR REPLACE PROCEDURE test_tirae IS 2 naxloops NUMBER :- 5000; 3 loopcount NUMBER(6,e) := в; it starttime CHAR(5) ; 5 endtine CHflR(5) ; 6 /* Note that since the start and end tines are defined in terms of the number of seconds since midnight, this routine will not work if the run time crosses ouer midnight.



I9



runtime NUMBER; H processrate NUMBER (2 8, 18) ; 11 12 BEGIN 1 13 starttine :- TO CHAR(SVSDATE, 'SSSSS ) ; 14 LOOP 15 loopcount :- loopcount +1 ; 16 INSERT INTO plsqUOl timetab (C1, C2,C3) 17 UALUES (loopcount, 'TEST ENTRY 1 . SYSDATE); COMMIT; 18 IF loopcount >= naxloops THEN 19 EXIT;' 2* 21 .'•: '. END IF; . f,,r.n: , •.*.•,.• !Ж ,M, -;/r. >•.-,-; 22 END LOOP; 23 COMMIT; 21 endtine := TO_CHflR(SYSDATE, 'SSSSS1 ) ; 25 runtime :- TO_NUMBER(endtine)-TO_NUMBEH(starttime); 26 dbms_output.put_line( runtime || ' seconds' ); 27 processrate := naxloops / runtime; 28 INSERT INTO plsql101_timetab (C1, CZ, C3) UALUES 29 (loopcount+1 , Зв TO_CHAR(processrate, ' 9999999999 ' ) 1 1 ' records per second1, 31 SVSDATE 32 ); 33 END test time; 34 Procedure created. SQL> EXECUTE test_time; 6 seconds PL/SQL procedure successfully completed. SQL> SELECT » FROM plsqliei_timetab 2 WHERE C1 > 5BOO; С1 С2 5вЦ



C3



. т



i • '



833 records per second 22-OCT-OO



SQL>



Рис. 9.6. Измерение скорости выполнения вставок в PL/SQL-программе



Глава 9



314



-.*



esc



• * Oiacle SQL-Plus File £di( Seatch Options Help SQL> TRUNCATE TABLE plsql181_tinetab;



" ' " i!



'Table truncated.



. • • : - . .



ГГ



-V ,•



!



• .



.



'



.-



SQL> COMMIT;



Commit complete.



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



SQL> SET SERUEROUTPUT ON SQL> BEGIN



.'



.



.



2 FOR trial count IN 1..18 3 LOOP 4 test time; 5 COMMIT; 6 END LOOP; „ 7 END; 8 / 7 seconds 8 seconds 7 seconds' 7 seconds 7 seconds 6 seconds 7 seconds 8 seconds 7 seconds 8 seconds



-- • ."



•.v -» ..• .



• ' ' ' ' '.



,



'•



•-



-.



.;



.



» :



'-.;".-



PL/SQL procedure successfully completed. SQL> SELECT 2 FROM 3 4



•'-'•,''.



* plsql181 timetab



• '•



WHERE C1 > 5080 ORDER BV c3;



4







.-'.''•



. ' •



' .



_; -



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



.



• 'i



;;. '



: ' .



.







-•_, .



-•



C3



714 records 625 records 714 records 714 records 714 records 833 records 714 records 625 records 714 records 625 records



per per per per per per per per per per



18 rows selected.



second second second second second second second second second second



22-OCT-OO 22-OCT-OO 22-OCT-08 22-OCT-88 22-OCT-OO 22-OCT-80 22-OCT-08 22-OCT-OO 22-OCT-80 22-OCT-08



V



• ' • , • " • ' < ;'• , •" -•



\







SQL>



-dJ



• •



.. • •



C1 C2



5081 5001 5801 5881 5881 5001 5081 5801 5881 5881



.







;



.



'• :



s .



.. • • .',-,*. ' ;



•> '



/'



-.'.''



: V



Рис. 9.7. Результаты теста скорости, состоящего из десяти итераций



: 4



'



: , ' '• 'A



Л



Другие средства 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 small_order_amt large_order_amt small_disct large_disct N



BODY plsql!01_pack IS NUMBER (8, 2) := 400; NUMBER (8, 2) := 1000; NUMBER (4, 2) := 1; UMBER (4, 2) := 5;



CURSOR PKG_PER_CUR RETURN plsql!01_person%ROWTYPE IS SELECT * FROM plsqllOljperson; -/':..,..,



,



;



.



,-



FUNCTION pkg_comp_discounts (order_amt NUMBER) RETURN1 NUMBER IS BEGIN IF (order_amt < large_order_amt



AND"



order_amt >- small_order_amt) THEN RETURN (order_amt * small_disct / 100) ; ELSIF (order_amt >= large_order_amt) THEN RETURN (order_amt * large_disct / 10Q) ; ELSE RETURN (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) IS hist_ord_avg NUMBER(8,2) := 0; 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; ..--



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.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; BEGIN SELECT AVG(tab2.product_price * tabl.quantity) avg_order INTO hist ord_avg FROM plsqllOl purchase_archive tabl, plsql!01_product tab2 WHERE 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; EXCEPTION WHEN NO_DATA_FOUND THEN a_perform.status := 'Exceptions found1;' END pkg_compute_perform;



BEGIN /* ДаФа первой загрузки пакета */ DATE_LOADED := SYSDATE; END plsql!01_pack; DECLARE one_perform plsql!01_pack.pkg_perform_type; cursale char(8); disct char(8); perf char(8); , BEGIN dbms output.put line('Code' || ' ' II 'Last Name 1 I I '.'II 'Total Sales' || 1 1 I I 'Discounts' || ' ' I I 'Performance%' || I '" I I 'Errors?'); FOR person_rec IN plsql!01_pack.PKG_PER_CUR LOOP 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 I one_perform.person_name || I I .11 cursale || 1 ' II disct ,| | perf



I one perform.status); END LOOP;



12 Зак. 725



Глава9



320



dbms_output.put_line('Pkg load date seconds ' || 1 TO_CHAR(plsqll01_pack.DATE_LOADED, SSSSS )); dbms_output.put_line('System date seconds ' || TO CHAR(SYSDATE, ' SSSSS')); END; /



mma



* Oracle SOL'Plus



: : : •FJe Ed» Search ^ptioni И**> •' :;' , * V.' 'x'^-'d FOR person_rec IN plsql101_pack.pkg per_cur 19 20 LOOP 21 plsq!101_pack.pkg_conpute_perf orn(person_rec, one_perform) ;



22 23 24 25 26 27 28 29 3D 31 32 33 34 35 36 37 38 39 40



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 ' II one_perf oi-n.pei-son_nane 1 1 ' ' II cursale 1 1 disct perf END LOOP;



one_perforn. status) ;



dbns_output.put line('Pkg load date seconds ' ||



42 TO_CHftR ( plsqii Ol_pack .date.loaded , ' SSSSS ' ) > ; 43 44 dbns_output.put_line{' System date seconds ' || 45 TO_CHflR(SVSDftTE, 'SSSSS')); 46 47 END; 48



Code Last Name Total Sales Discounts СЙ Atlas 313.31 0 Gfl Anderson 865.24 8.6524 BB Barkenhagen 5456.25 272.8125 LB Baxter 1455 72.75 LN Norton 1455 72.75 Pkg load date seconds 39571 System date seconds 39571



Performance^ Errors? 7.7 All fine -58.8 All fine 4446.9 All fine All fine Erroneous or no history



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 операторы [EXCEPTION WHEN имя_исключения 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_purchase ADD 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_trigger BEFORE INSERT OR UPDATE ON plsql!01_purchase FOR EACH ROW DECLARE no_name_change EXCEPTION; quant_change NUMBER(5) := 0; BEGIN /* He разрешать изменение product_name в заказах. Возбудить исключение и вернуть значения к исходным. */ IF (UPDATING AND (:NEW.product_name :OLD.product_name)) THEN RAISE no_name_change; END IF; /* Создать номер заказа для старых ненумерованных заказов, а также для новых заказов, не имеющих номеров. */ IF (((UPDATING) AND (:OLD.ORDER_NUMBER IS NULL)) OR ((INSERTING) AND (:NEW.ORDER_NUMBER IS NULL))) THEN SELECT order_num_seq.NEXTVAL INTO :NEW.ORDER_NUMBER FROM 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_audit VALUES ( : NEW . ORDER_NUMBER, : NEW . salesperson, USER, USERENV ( ' TERMINAL ' ) , quant_change, SYSDATE) ; EXCEPTION WHEN no_name_change THEN 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_purchase VALUES ('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. £ SELECT * FROM plsqllB1_purchase WHERE salesperson - 'LN'; PRODUCTJMHE



SAL PURCHASE.



Medium Wodget Round Snaphoo



LN LN



QUANTITV ORDER_NUMBER



18-AUG-82 15-NOU-G2



28 2



12



SQL> SELECT • FROM plsqll01 a u d i t ; ORDER_NUMBCR PER USER_NAHE 12 LN



USER MHCHINE



PLSQL181



CHANGE IN QUHNT TRANSACTI



ALPHANERD



2 22-OCT-B8



1



SQL> UPDATE plsqll81 purchase SET salesperson - ЧВ 2 WHERE salesperson * 'CO' AND quantity - 1; 2 rows updated. SQL> SELECT » FROM plsqllB1_purchase WHERE salesperson - ' C A ' ; PRODUCTJMHE



SAL PURCHASE.



Round Chrome Snaphoo



CR



QUAHTITV ORDER_NUI«ER



16-JUL-03



5



SQL> SELECT » FROM plsqllB1_audit; ORDERJWHBER PER USER_NAHE 12 LN 13 LB 1* LB



USER_MACHINE



PLSQL1B1 PLSQL101 PLSQL181



CHANGE_IN_QUANT TRANSACTI



ALPHANERD ALPHANERD ALPHAHERD



2 22-OCT-B8 8 22-ОСГ-В8 8 22-OCT-8B



SQL> UPDATE plsqllB1_purchase SET quantity - 28 UHERE salesperson - 'BB'; 1 row updated.



,



SQL> SELECT » FROM plsqllB1_purchase WHERE salesperson - ' B B ' ; PRODUCT JHHME



SAL PURCHASE.



Medium Wodget



BB



1U-JUL-03



QUANTITY ORDERJWMBER 28



15



SQL> SELECT * FROM plsqllB1_audit; DRDER_NUHBER PER USER_NAHE 12 13 14 15



USER_HACHINE



LN PLSQL1B1 LB PLSQL1B1 LB PLSQL1B1 BB PLSQL1B1



CHAHGE_IN_QUANT TRANSACTI



ALPHANERD ALPHANERD ALPHANERD ALPHANERD



2 8 8 -55



22-OCT-BB 22-OCT-B8 22-OCT-D8 22-OCT-88



SQL> UPDATE plsq!181 purchase SET product_nane - 'Round Snaphoo' 2 WHERE salesperson - 'BB'; Change OF product name not allowed Aborting and resetting to old values 1 row updated. SQL> SELECT « FROM plsqllB1_purchase UHERE salesperson - '88'; PRODUCT_NAHE



SAL PURCHASE.



Hediun Wodget



BB



Ht-JUL-03



QUANTITV ORDER_HUMBER



2B



15



SQL> SELECT » FROM plsqllB1_audit; ORDER_HUH8ER PER USER_NANE 12 13 14 15



LN LB LB BB



PLSQL1B1 PLSQL181 PLSQL1B1 PLSQL1B1



USER_MACHINE ALPHANERD ALPHANERD ALPHANERD ALPHANERD



CHAHGE_IN_QUANT TRANSACTI 2 8 8 -55



SQL>



Рис. 9.10. Триггер, запрещающий изменение названия товара при обновлении заказов



22-ОСТ-ев 22-OCT-BB 22-OCT-BB 22-OCT-88



Другие средства PL/SQL



327



Давайте поговорим о том, что здесь происходит. Сначала к таблице покупок добавляется столбец ORDER_NUMBER. Затем создается таблица для аудита записей, содержащая имя вошедшего в систему пользователя. Имя пользователя вводится с помощью функции USER, а имя его компьютера или терминала — с помощью функции USERENV, вызываемой с параметром TERMINAL1. Также вводятся личный код продавца, изменение в количестве заказанного товара, номер заказа и дата изменения. Триггер не позволяет изменятьproduct_name и автоматически генерирует номера для новых заказов, а также старых записей в таблице заказов, которые не были пронумерованы. При успешном обновлении или вставке триггер вставляет значения в таблицу аудита. Ключевые слова UPDATING и INSERTING сообщают нам, что послужило запускающим действием — обновление или вставка. Сначала триггер был протестирован путем вставки строки в таблицу PLSQL101_PURCHASEn сравнения старых значений с новыми. Затем были выполнены два простых обновления, а в заключение — попытка изменить название товара в заказе.



Модификация триггеров Для триггеров, как и для представлений, не существует команды модификации. Старый триггер просто заменяется новым с помощью команды CREATE OR 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 показано диалоговое окно входа и отмечено, какие поля каким пунктам соответствуют.



IPLSQLIOI



User Name: Password:



|your_ho8t_name|



Host Suing:



OK



Cancel



Рис. 9.11. Информация о соединении SQL'Plus, используемая при создании ODBC-соединения Чтобы создать ODBC-соединение, выполните следующие действия: 1. Запустите Microsoft ODBC Administrator. Для этого откройте меню Start, перейдите в раздел Programs и выберите программную группу Oracle. Найдите и запустите программу с названием Microsoft ODBC Data Source Administrator. Вы должны увидеть экран, аналогичный показанному на рис. 9.12. t 'ЦШПС Data Souice Adminisltatoi .



UserDSN | S^tomDSN | FileDSN | Drivers | Tracing j .Connection Рос** | About | Jser Data Sources: Name | Drivei 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 (".mdbl MS Access 37 Database Microsoft Access Driver (".mdbj Paradox Files Microsoft Paradox Driver (N.db) Text Files Microsoft Text Driver ('.bit; ".csvj Visual FoxPro Database Microsoft Visual FoxPro Driver Visual FoxPro Tables Micro«ofl Visual FoxPro Oliver



Ajjd... ,



1.



Bemove Configure...



yBEfl An ODBC User data source stores informatbn about how to conned to r^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



|



Рис. 9.12. Microsoft ODBC Data Source Administrator



Help



Глава 9



330



2. Если на вашем компьютере работает несколько человек и вы хотите, чтобы ODBC-соединение было доступно всем пользователям, щелкните на вкладке System DSN в верхней части окна. Если ODBC-соединение будет использоваться только вами, оставайтесь на вкладке User DSN. 3. Щелкните на кнопке Add. При этом откроется диалоговое окно Create New Data Source, показанное на рис. 9.13. Выберите источник данных с именем Oracle ODBC Driver и щелкните на кнопке Finish. 4. Теперь вы должны увидеть диалоговое окно с заголовком OracleS ODBC Driver Setup, показанное на рис. 9.14. Первое поле называется Data Source Name. Введите туда имя, по которому вы хотели бы ссылаться на данное ODBC-соединение. Для простоты в качестве имени ODBC-соединения обычно указывают имя базы данных. Учитывая эту рекомендацию, введите в поле Data Source Name строкуPLSQL101. 5. Перейдите к полю Service Name. Введите туда имя базы данных Oracle, которое вы использовали при входе в SQL*Plus. Для этого упражнения следует ввести значение PLSQL101. 6. Щелкните на кнопке ОКдля завершения конфигурирования и возврата к диалоговому окну ODBC Data Source Administrator. В списке Data Sources вы должны увидеть только что созданный источник данных. 7. Щелкните на кнопке ОК, чтобы закрыть диалоговое окно ODBC Data Source 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 Oracle Microsoft Paradox Driver (".do) Microsoft Text Driver (".txt; ".csv) Microsoft Visual FoxPro Driver Oracle ODBC Driver for Rdb SQL Server



Рис. 9.13. Создание нового источника данных



331



Другие средства PL/SQL ВШВ^1П« flowSet belp



,1^1 X)



QloSJHJ ХНЫ®] 45|tf| Connect.



Disconnect



All Tables



1 User Tables 1



Execute...



Next Row Set 1



1 • to* I



*



1 2 3 H S







-



TBBLE_CflI







TflBLE_SCHEM











TABLE.MHE



PLSQL181 PLSQL1I1 PLSQL1I1 PLSQL101 PLSQL1B1



. •: ^



PLSQL1H AUDIT PLSQL1I1~LOG PLSQL101~OLD ITEH PLSQLH1 PERSON 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).



Глава 9



334



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 NUMBER PE~RSON_Coi5e~ USER_NAME USER_MACHINE CHANGE_IN_QUANT TRANSACTION TIME



OK Cancel



To ensure data integrity and to update records, you must choose a field or Fields that uniquely identify each record. Select up to ten fields.



Рис. 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*



STables



|



^Queries



]



Sperms



-IfflX



|



В Reports



|



3 Macros



]



«$ Modules



j •



»ф KSQ1101_PL5Q1101_AUDITJ



gl»"



|



»ф RSQL101_PL5QL101_OLDJTEM



pK)gn



j



У



1



»H PLSQll01^PLSQI.mi_PERSON »Ф PLSQI.101J>L5QLmi_PROOUCT »Ф aSQL101_PLSQL101_PURCMASE >ф PLSQC101_PLSQL101_PURCHASE_ARCHIVE >ф PL5QL101J>L5QL101_TIMETAB



1



ie



eed,



Рис. 9.19. Связанные таблицы в Access



[



p-f— f



• .



ЙЖГ



!:







Глава 9



336 ull Access -IPLSQL1U1 PLSQL101 PUflCHASE 1 FJe Edit И"» Insert Format Records Idols Window ИФ



PRODUCTJIAHE



v м | »*



I SALESPERSON | PURCHASE_DATE | QUANTITY | ORDER



Medium Wodget Chrome Phoobar Small Widget Medium Wodget Round Chrome Snaphoo Small Widget Medium Wodget



CA BB GA GA LB CA CA 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



OK



Databases I Queries | MS Access 97 Database' ORa_ODBC" Paradox Files (not sharable) Paiadox Files* PLSQL101" Тек) Files (not sharable] Text FJes" Visual FoxPro DatabaseVisual FoxPro Tables" Gp|



J



Cancel Browse...



j



J



F Use the Query Wizard to create/edit queries



Рис. 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? Columns in your query:



Available tabtes and columns: 6! PLSQL101 .PRODUCT



jj.



PRODUCT NAME



>



Я PISQL1 01 .PURCHASE — — Ш PLSQL101 PURCHASE ARCHIVE -J « ffl PLSQL101_PURCHASE_ARCHIVE Й PLSQL10lIPURCHASE_LOG -J



j



SALESPERSON PURCHASE DATE QUANTITY ORDER NUMBER



^review of data in selected column:



Gp|



Pjeviev* Now j



•< §ack



1



^eKt>



Cared



{



|



Рис. 9.22. Выбор столбцов Oracle для импорта в Excel 7. В следующем диалоговом окне предлагается указать до трех столбцов сортировки. Щелкните на кнопке Next. 8. В последнем диалоговом окне мастера запросов выберите опцию Return Data to Microsoft Excel и щелкните на кнопке Finish. 9. В завершение вы увидите диалоговое окно с вопросом о том, куда следует поместить данные из Oracle. Щелкните на кнопке ОК. 10. Теперь данные Oracle должны находиться в таблице Excel, как показано на рис. 9.23.



-iJlx't



JpFJe £* Bew insert Format tools E*a Window Help



]|D а* в [sa У *la ft ^ j ю , o. . | 4 «f | Z /. Й' "iiHl f Г© " jj^ jJArial



- 10



Al



1 1 2 3 A 5 6 7 8 9



_J



'||H



7 II •* » S Ш | » % i « Л



:



*| E- *•• Д,г '•



. »



A



PRODUCT1 NAME Small Widget Medium Wodget Chrome Phoobar Small Widget Medium Wodget Round Chrome Snaphoo Small Widget Medium Wodget



8



SAL CA BB GA GA LB CA CA LN



J



С



J



D



J_



E



ESPERSON PURCH"ASE DATE QUANTITY ORDER NUMBER 7/14/20030:00 7/1 4/2003 0:00 7/14/20030:00 7/1 5/2003 0:00 7/15/20030:00 7/16/2003 0:00 7/17/20030:00 8/18/20020:00



1



RT



,



1 75 2 8 20 5 1 20



••



10 11 12



-



'



13 ватятяят •x]



-Ц-



:9&tk\ 1 Ж ®



i



-1



16,



17 Jt^ М>|Л S**MI / — ' '



~~ i < i



Ready



Рис. 9.23. Данные Oracle, импортированные в Excel



j



,



№м|



- 1



>1Г



!Г~~Г~~ /s



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_catalog. Для установления такой связи столбец 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, trigger statement) Событие (например, выполнение оператора 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 Press ITION