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

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

295 101 5MB

Russian Pages 369 Year 2001

Report DMCA / Copyright

DOWNLOAD PDF FILE

Recommend Papers

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

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
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

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

.'  •  :  .m ii  пйЕ  незамужняя  свс pa.v  \s] : Перед  тем.  как j гтеть  на    J"  :| Восточное  побе| ежье.  нужно 6  Свен Шрам и  (555) 6; 8-9012  sschltmm Smaii  net  убедиться,  что  о i  HE  работает  ^И 2  Майкл 

Строки

Хабнер

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

'  И  . 

,•



—  Таблица

'  >lDi

Т

Поля

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

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

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

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

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



Глава 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.

22

Глава 2

тшс

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)



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)

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

35

INSERT INTO plsql!01_product VALUES ('Product Name 1',  1); INSERT INTO plsql!01_product VALUES ('Product Name 2', 2.5); INSERT INTO plsql!01_product VALUES ('Product Name 3',  50.75); INSERT INTO plsql!01_product VALUES  ('Product Name 4', 99.99); SELECT * FROM plsql!01_product; DROP TABLE plsql!01_product; Когда вы закончите,  экран должен выглядеть так,  как показано на рис.  2.7.

Ж  Oiacle  SQL'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 Name 1 Product Name 2 Product Name 3 Product Name 4

PRODUCT  PRICE  PURCHASE 1  05-NOU-00 2.5  29-JUN-B1 58.75  18-DEC-B2 99.99  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, 

r';> .;.,ќ .,

purchase_date DATE i В результате экран будет выглядеть так,  как показано на рис.  2.10.  Чтобы  , проверить,  к чему привело  объявление столбца как  NOT  NULL,  вы должны уметь вставлять запись,  не  содержащую никаких данных.  Это и будет первой темой следующего раздела. *  Oracle  SQL-Plus File  Edit  Search  Options  Help SQL>  DROP  TABLE  plsql101_purchase;

Table dropped. SQL> SQL>  СВЕЙТЕ  TABLE  plsq11B1_purchase  (



product_nane  UftRCHflR2(25)  NOT  NULL,

3  i»  5 

product_price NUHBER(I»,2)  NOT NULL, purchase_date  DATE )

6 ;

Table  created.

SQL>

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

НГ-Ш

42 

Глава 2

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

Как вставлять записи с null-значениями Вы уже знаете, что null-значение соответствует пустому атрибуту — например, отсутствующей дате рождения в записи о служащем. В своей практике вы наверняка столкнетесь со множеством ситуаций, где потребуется вставлять записи  с  null-значениями в  определенных столбцах.  Есть два способа решения этой  задачи. Первый заключается в том, чтобы использовать в операторе INSERT слово "NULL" вместо реального значения.  Например,  в последней созданной вами таблице (PLSQL101_PURCHASE) столбцы с названием и ценой товара являются обязательными, тогда как столбец с датой покупки — нет. Следовательно, можно вставить запись только с первыми двумя атрибутами: INSERT INTO plsql!01_purchase VALUES  ('Product Name 1',  1,  NULL).; SELECT * FROM plsqll01_purchase;

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

;*. Oracle SQL-Plus File  Edit  Search  Options  Help  . SQL>  INSERT  INTO  plsql1B1_purchase  UALUES  ('Product  Name  1',  1,  NULL); 1  row  created. SQL> SQL>  SELECT  »  FROM  plsqllB1_purchase; PRODUCT_NflME  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> INSERT INTO plsq!1B1_purchase (product_nane, product_prlce)  QL> INSERT INTO plsqll 2 UflLUES  С Product  Name  2 ' ,  2.5);

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_PRICE  PURCHASE.

1 2.5

Product Name 3 

58.75

Product Name  i» 

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



('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 

PRODUCT  PRICE+SflLES  TUX

1.88 2.71 51». 94

Name  1 Name  2 Name  3 Name  4

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

180-PRODUCT  PRICE

99 97.5

Product Name  1 Product  Name  2 Product Name  3 Product  Name  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  | | S ALESPERSON Product  Name  1  was  sold  by ftB Product  Name  2  was  sold  by  CD Product  Name  3  uas  sold  by  EF Product  Name  4  Mas  sold  by  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 Mane 1 was sold by ftB Product Name 2 was sold by CD Product Name 3 was sold by EF Product Name 4 was sold by GH SQL>

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

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

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

53

Имя таблицы или столбца может иметь длину до 30 символов, Оно; должно начинаться с буквы и может содержать буквы, цифры и ограниченный набор специальных символов, наиболее полезным из которых является символ подчеркивания  О.  позволяющий визуально разделять слова в имени.  Пробелы  в именах таблиц и столбцов недопустимы. Oracle рассматривает символы верхнего и нижнего регистров как одинаковые. Определенное количество слов зарезервировано и не может использоваться в качестве имен таблиц или столбцов. В частности, к ним относятся команды (например, CREATE) и имена объектов (например, ROW). Зарезервированных слов слишком много, и запомнить их все большинству людей не под силу. Случайно употребив одно из них, вы сразу узнаете об этом, поскольку вместо нормального завершения команды Oracle выдаст сообщение об ошибке с фразой "Invalid table name" или "Invalid column name". Если проблема связана с именем таблицы, добавьте в начале имени аббревиатуру, обозначающую систему, частью которой является таблица (например, AP_ADMIN вместо ADMIN).  Если проблема связана с именем столбца, добавьте к имени одно-два слова, чтобы лучше описать содержимое столбца.  ,-, Создавая любую таблицу, вы автоматически становитесь ее владельцем. Все принадлежащие вам таблицы должны иметь уникальные имена; у вас не может быть двух таблиц с одним и тем же именем.  (Однако имена таблиц,  принадле^ жащих двум разным пользователям Oracle, могут совпадать.) Каждый столбец в пределах одной таблицы должен иметь уникальное имя. В разных таблицах могут использоваться одни и те же столбцы. Имена таблиц лучше всего записывать в единственном, а не множественном числе:  например,  таблицу  служащих  следует  назвать  EMPLOYEE,  а  не EMPLOYEES. Кроме того, не нужно включать в имя таблицы слова TABLE и DATA, поскольку все, о чем они сообщают, следует из самого факта работы с таблицей. При создании таблицы вы должны указать тип данных и длинукаждого столбца. Таблицы Oracle могут хранить любые разновидности данных — текст, числа, даты, изображения, звуковые файлы и т. д. Каждый тип данных имеет  определенный  набор  свойств.  Самые  распространенные  типы  данных таблицы — это текст, числа и даты. Текстовый  столбец  может содержать буквы,  цифры,  пробелы  и  специальные символы — все, что можно ввести с клавиатуры. Когда в текстовый столбец вводится число, оно также рассматривается как текст. Числа в текстовых столбцах невозможно складывать, усреднять или выполнять над ними какие-либо другие математические операции.  В текстовые столбцы обычно помещаются числа, содержащие нецифровые символы — знаки математических операций, буквенные символы или пробелы. Широко известными примерами текстовых значений,  содержащих большое  количество чисел,  являются  телефонные  номера, номера социального обеспечения и счетов. Если числовое значение никогда  не  предполагается  использовать  в  математических  операциях,  оно является хорошим кандидатом на помещение в текстовый столбец. Существуют два основных типа текстовых столбцов: с фиксированной и^фз ременной длиной. Для создания столбца фиксированной длины нужно указать в команде CREATE TABLE тип данных CHAR. Длина текста в столбцах CHAR всегда равна длине, указанной в объявлении столбца. Более короткие данные

54 

Глава 2

дополняются пробелами. Это может привести к бесполезной трате места, поэтому  столбцы  CHAR  уместны  только  в  тех  случаях,  когда  все  данные  будут иметь одинаковую длину, как, например, обозначения пола или коды штатов. Большинство текстовых столбцов будут содержать данные переменной длины, и здесь следует использовать тип VARCHAR2. Если длина текста, который вы намереваетесь хранить в столбце, превышает 2000 символов (это максимум для типа VARCHAR2), можно воспользоваться типом LONG,  который позволяет хранить до двух миллиардов символов в одном поле. Текстовое  значение  обычно  называется  строкой.  В  SQL-командах строки всегда заключаются в одиночные кавычки. Простой текст, содержащий только те символы, которые можно найти на клавиатуре (без символов форматирования, вставляемых текстовыми процессорами и электронными таблицами), часто называется ASCJI-текстом. (ASCII — это сокращение от American 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  |  | "  "  I I

•;,'.; 

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

58

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

],{

Объяснение  *,и(). 6.  В. 

К математическим операторам относятся символы  +,  -,

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

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

1

-

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

60. 

.._.„ 

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

• • • - - • 

ГлаваЗ

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

Ограничение диапазона выбираемых записей Одной из наиболее распространенных операций, которые вы будете выполнять при выборе записей из таблицы, является получение определенного подмножества  записей.  Это  подмножество  будет  постоянно  меняться  в зависимости от поставленных вопросов (например: "Какие клиенты не получали от меня сообщений более двух недель?" или "Какие товары продавались партиями, содержащими более чем  100 штук за последние 30 дней?").  Подобная фильтрация записей выполняется путем добавления к оператору SELECT специальной конструкции WHERE. В ней указываются условия (conditions), которым  должны  удовлетворять  отображаемые  записи.  Синтаксис  выглядит следующим образом: SELECT  столбцы  FROM  имя_таблицы  WHERE условие(я); Например, можно потребовать, чтобы дата последнего контакта с клиентом отстояла от текущей более чем на две недели; или чтобы дата продажи товара приходилась на последние  30 дней,  и при этом  общее  проданное крличество превышало  100 штук. Давайте выполним несколько упражнений. Чтобы вам было с чем работать, создадим заново таблицу PLSQLIOIJPRODUCT, увеличив число столбцов, и поместим в нее записи: DROP. TABLE plsqliaijproduct; CREATE' ТАВЪЕ plsqll01_product ( product_name VARCHAR2(25), product_price NUMBER(4,2), quantity_on_hand NUMBER(5,0), last_stockdate  DATE

INSERT INTO plsql!01_product VALUES ('Small Widget',  99, 1,  '15-JAN-03'); INSERT INTO plsql!01_product VALUES ('Medium Wodget1,  75,  1000,  '15-JAN-02'); INSERT INTO plsql!01_product VALUES ,,,rtr .  J'Cjtirpme.  Phoobar'.,.50,  100,,  '  15-JAN-03')  ;   .;  INSERT INTO plsq!101_product VALUES ('Round Chrome Snaphoo',  25,  10000,  null);

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

61

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

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

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

:;    .

Чтобы выбрать из тестовой таблицы все записи,  где  количество товара равно  1, введите следующую команду: SELECT * FROM plsql!01_product WHERE quantity_on_hand = 1; Результат показан на рис.  3.1. Теперь, чтобы лучше освоить эту технику, выберите записи, в которых цена равна 25. Следующий шаг будет состоять в  выборе записей со значениями, которые больше или меньше определенной величины. Например, чтобы найти товары, запасы которых требуют пополнения, можно использовать следующую команду: SELECT  *  FROM  plsql'101_ptoduct WHERE  quantity_on_hand    SELECT  *  FROM  plsqll 01  product 2  WHERE  quantity_on_hand 



'  - 

•  ' 

.  : 

:

•:•'•• 

• 

;





.

;

.

: • - . . , 

.



' • - • . ' 

.

::•'.. 





«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 » 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 14-JUL-03  Cfl 75 1U-JUL-83  BB 2 -U-JUL-B3  GO 8  15-JIIL-83  Gfl 20 15-JUL-03  LB 2 16-JUL-03  Си 25 16-JUL-83 LB 2 17-JUL-03 88

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 1U-JUL-03 BB 2  1I|-JUL-B3  GA 8  15-JUL-03  GA 20  15-JUL-03  LB 2  16-JUL-03 CA 25  16-JUL-03  LB 2  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 1 WHERE  purchase_date > 45-JUL-03 ; 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 1U-JUL-83  Cfl 75 1H-JUL-B3 BB 2 Ht-JUL-83  Gfl 8  15-JUL-B3  Gfl 28 15-JUL-83 LB

SQL>

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

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

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

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

79

I  ^ TRUNCATE TABLE pisql!01_purchase; SELECT * FROM plsqll01_purchase;

Предупреждение Команда  TRUNCATE необратима! Используйте ее  только в случае действительной  необходимости!

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

Отмена транзакций DML Когда вы вставляете, обновляете или удаляете данные,  Oracle не выполняет эти изменения немедленно. Вам кажется, что изменения произошли тотчас же; если ввести команду SELECT, они будут отражены в выходных данных. Однако на самом деле изменения хранятся во временной области памяти и будут применены к таблице только в ответ на одну из нескольких специальных команд. Скоро вы узнаете, что это за команды, а пока давайте посмотрим, что происходит до выдачи одной из них. Если вы только что начали читать эту главу и у вас еще  нет учебной таблицы PLSQL101_PURCHASE,  создайте ее,  используя следующую команду: CREATE TABLE plsql!01_purchase  ( product_name  VARCHAR2(25), quantity  NUMBER(4,2), purchase_date DATE, salesperson  VARCHAR2(3)

'

Отмена  в  Oracle  выполняется  с  помощью  команды  ROLLBACK  (Откат). Производя откат одной  и  более транзакций,  вы  сообщаете Oracle,  что  они  не должны применяться к базе данных. Чтобы увидеть,  как это делается, введите следующие команды: '3 INSERT  INTO  plsql!01_purchase  VALUES ('Small Widget1, 1,  44-JUL-03',  'CA'); INSERT  INTO plsql!01_purchase  VALUES ('Medium Wodget',  75,  44-JUL-03',  'BB'); SELECT * FROM plsql!01_purchase; ROLLBACK; SELECT * FROM plsql!01_purchase;

4 Зак. 725



ќ

Глава 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  1i»-JUL-03 CA 75 14-JUL-83 BB 2 14-JUL-03 GA 8  15-JUL-B3 GA 2» 1S-JUL-83 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 

SQL> SOL>

PROOUCT.NAHE

ќ  ; ;

1 ro» created.

gUANTITV PURCHASE. SAL 1 1J.-JUL-03 CA 75 1»-JUL-»3 BB 2 1»-JUL-e3 CA 1 15-JUL-B3 CA 21 15-JUL-.3 LI

sgL> sgt> sgi>

ш SQ->

i  '/  .

SQL>  select  ќ Fro» rLSQL1l1.PURCHASE;

S«L>

SQL> 

.......... , 



, . , 

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

.

.

.

.

ќ

__

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

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

1 H-JUL-Ю CA 75 1»-JUL--3 II 2 -U-JUL-n CA 1 15-JUL-U CA 21 15-JUL-n LI .  I,  ,

ни

sgL> con.it; Comit couple tt.

.'  ; 

$gt> SQL> SQL> seltct ќ FroH

.- ; PLSgL1H_PURCHRSE:

'RODUCT NAME

'  - '  '" (ч * Д

:^ 

QUANTITY PURCHASE. SAL

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

  ;

:  ќ 

:

\4L>

sgL>

,

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. 

  >  ' '  ќ'ќ'.'  : .  ќ:  '  '  . - . ' ' .

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

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

.ii 

!

sgL>

IQL> SQL>

sgL>  sgL> ML» sgL>

 

HP

sgL>

JQL>

.: '  .

j .  ' ќ

sgi> select i Fran PLSQI1I1_P-RCHASE;

sgi>  sgL>

SPL> 'S»L> 

.ќ  ,;; "

 

',  ќ 

SQL) SQL> SQL>

1 H-JUL-Ю CA 75 1»-JUL-«3 IB 2 H-JUL-II3 CR 1 15-JUL-n CA 21 15-JUL-03 LI

sgL> sgi> SQL> sgi> sqi>

ќ ќ ќ ќ ќ . ќ ќ ќ . ќ ќ ќ . С Ж Е ;   ;:

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

sgL> sgi>

ќ.  ќ  ќ  :ќќ'ќ'  .-:  :ќ  ќ  /ќ-  ќ  :

;fi

»«-> ќ I I

.;'S" 

"".;",> 

ќ':- 





.

"-.-.".

ќ  j

.iH

Рис. 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. (Какая программа в действительности откроется для редактирования текста — зависит от

Глава4

92 Я 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  product_nmae FROM  plsql!01_produtc WHERE  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 1* SELECT SQL>  c/na/an 1» SELECT SQL> 2 2» FROM SQL>  c/tc/ct 2*  FROM SQL> /

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



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 Small  Widget 15-JflN-03

99 

1

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

Chrome  Phoobar 15-JflN-83 PRODUCT_HftME

75 





1000 ' 

50 





.

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;

103

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

1(1,880

Chrome Snaphoo SQL> SQL> COLUMN product nane FORMAT A10 WORD_URAP HEADING 'Name' JUSTIFV CENTER SQL>  COLUMN product_price FORMAT $99.99  HEADING  'Price1  JUSTIFV  RIGHT SQL> COLUMN quantity_on_hand FORMAT 99,999 HEADING  'On|Hand'  JUSTIFV RIGHT SQL> COLUMN last_stock_date HEADING 'Last(Stock(Date' JUSTIFV RIGHT SQL> SQL>  SELECT  » FROM plsq!181_product; Last

Nane 

Price 

On  Hand 

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 

107

COLUMN product_name OFF COLUMN product_price  OFF COLUMN quantity_on_hand OFF COLUMN last_stock_date OFF SELECT * FROM plsqllOl product;

Буферизация выходных данных на диске Буферизация (spooling) — это процесс записи информации в дисковый файл. Иногда это удобно делать прямо из SQL*Plus, чтобы сохранить серию команд с результатами их выполнения или объемные выходные данные одной команды. (Если выходные данные умещаются на одном экране SQL*Plus,  можно просто выделить мышью нужный фрагмент, скопировать его в буфер обмена, а затем вставить  в любую  нужную  программу.  Возможно,  после  вставки  потребуется установить для этих данных моноширинный  шрифт, чтобы сохранить выравнивание  строк.) Команда  SPOOL  имеет  следующий  синтаксис: SPOOL  имя_буферного_файла При желании можете включить в имя буферного файла расширение (например, .sql или .ргп). Если расширение не указано, имя автоматически дополняется  расширением  .1st.  Кроме того,  имя буферного файла может содержать путь (path),  т.е.  имя  дискового  накопителя  и  каталога,  где  должен  быть  сохранен файл. Если путь не указан, буферный файл сохраняется в подкаталоге BIN  базового  каталога  Oracle. Чтобы  увидеть,  как  выполняется  буферизация,  введите  перечисленные ниже  команды.  Обратите  внимание,  что  после  команд  SPOOL  не  нужно  ставить  точку  с  запятой,  поскольку  они  являются  внутренними  командами SQL*Plus и  не  влияют на работу сервера Oracle. SPOOL c:\plsqll01_test.prn SELECT *  FROM plsql!01_product; SELECT *  FROM plsql!01_purchase; SPOOL OFF \

Примечание Если вы запускаете  SQL *Plus в  Unix,  путь  к буферному файлу будет иметь  примерно  такую  структуру: /uO 1/user/plsql 101_test. prn (Не забывайте, что в путях и именах файлов Unix учитывается регистр символов.) После выполнения этих команд запустите Windows Explorer или File 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,  0) plsql!01_product;

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

PRODUCT  PRICE

Small Widget  Medium Wodget  Chrome  Phoobar  Round Chrome Snaphoo  Extra Huge Mega Phoobar  » 

99 75 50 25 9.95

SQL> SQL> SELECT productќname,  ROUND(product_price,  0) 2  FROM  plsqll Byproduct; PRODUCT_HflME 

ROUND(PRODUCT_PRICE,8)

Small Widget  Medium Wodget  Chrome Phoobar  Round  Chrome  Snaphoo  Extra Huge Mega Phoobar  +  SQL> |

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

99 75 se 25 10

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

123

/

зывать любое число десятичных знаков, но некоторые значения имеют больше смысла, чем другие. Если указать отрицательное число, то функция ROUND начнет округление до десятичной точки, иначе говоря, числа будут округляться до ближайших десятков, сотен, тысяч и т. д. Лучший  способ  увидеть  связь  между  точностью,  задаваемой  в  функции ROUND, и изменением, которое производится в обрабатываемом числе, — это применить функцию к числам, содержащим много десятичных знаков. Поскольку ваши тестовые таблицы не содержат записей с такими числами, да и нет никакого  смысла  создавать  подобные  записи,  мы  воспользуемся  таблицей DUAL. Введите перечисленные ниже команды, чтобы посмотреть, как отражается количество десятичных знаков,  указанных в функции ROUND,  на способе округления переданного ей числа. Результаты сведены в таблицу 5.1. SELECT ROUND(1234.5678, 4) FROM DUAL; SELECT ROUND(1234.5678,  3)  FROM DUAL; SELECT ROUND(1234.5678, 2)  FROM DUAL; SELECT ROUND(1234.5678,  1}  FROM DUAL; SELECT  ROUND(1234.5678,  0)  FROM DUAL; SELECT  ROUND(1234.5678,  -1)  FROM DUAL; SELECT ROUND(1234.5678,  -2)  FROM DUAL; SELECT ROUND(1234.5678,  -3)  FROM DUAL; Таблица 5.1. Результаты применения функции ROUND Функция ROUND 

Возвращаемое значение

ROUND(1234.5678,4) 

1234.5678

ROUND( 1234.5678, 3) 

1234.568

ROUND( 1234.5678, 2) 

1234.57

ROUND( 1234.5678,1) 

1234.6

ROUND( 1234.5678,0) 

1235

ROUND(1234.5678, -1) 

1230

ROUNDf 1234.5678,-2) 

1200

ROUNDf 1234.5678,-3} 

1000

TRUNC Функция  TRUNC  усекает  число,  понижая  его  точность.  Различие  между усечением и округлением проявляется, когда за последним из остающихся десятичных разрядов идет значение 5 и выше.  Округление привело бы к увеличению  содержимого последнего разряда на  1,  тогда  как при  усечении  этого  не происходит. Введите следующую серию команд, чтобы понять, о чем идет речь. Результаты сведены в таблицу 5.2.

124 

Глава 5

SELECT TRUNC(1234 .5678, 4) FROM DUAL; SELECT TRUNC (1234 .5678, 3) FROM DUAL; SELECT TRUNC(1234 .5678, 2) FROM DUAL; SELECT TRUNC(1234 .5678, 1) FROM DUAL; SELECT TRUNC(1234 .5678, 0) FROM DUAL; SELECT TRUNC(l-234 .5678, -1) FROM DUAL; SELECT TRUNC(1234 .5678, -2) FROM DUAL; SELECT TRUNC(1234.5678,  -3)  FROM DUAL; Таблица 5.2. Результаты применения функции TRUNC Функция TRUNC 

Возвращаемое значение

TRUNC( 1234.5678,4) 

1234.5678

TRUNC( 1234.5678,3) 

1234.567

TRUNC( 1234.5678, 2) 

1234.56

TRUNC( 1234.5678,1) 

1234.5

TRUNC(1234.5678,0) 

1234

TRUNC(1234.5678,-1) 

1230

TRUNC( 1234.5678, -2) 

1200

TRUNC( 1234.5678, -3) 

1000

Текстовые функции Текстовые функции, называемые в Oracle символьными функциями (character functions), оперируют с текстовыми строками. Чаще всего с текстовыми строками  требуется  делать  следующее:  изменять  регистр  символов  (на  верхний, нижний или смешанный); разбивать длинные строки на несколько более коротких подстрок; очищать текст, поступающий из внешнего источника, от избыточных пробелов в конце. Изучив описанные ниже текстовые функции, вы научитесь выполнять все эти операции.

UPPER, LOWER и INITCAP Эти три функции меняют регистр переданного им текста. Поскольку их назначение очевидно,  сразу приведу примеры: SELECT UPPER(product_name)  FROM plsqll01_product; SELECT LOWER(product_name)  FROM plsq!101_product; SELECT INITCAP(productjname)  FROM plsqll01_product; Результаты выполнения этих команд показаны на рис. 5.3. Как можно заметить,  функция  INITCAP в данном случае  ничего  не делает,  поскольку каждое слово  в  названиях  товаров  и  так  начинается  с  большой  буквы.  Способность этой функции наводить порядок в тексте гораздо лучше демонстрирует следующая команда:

Встроенные  функции  SQL 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 plsqll01_product SET  product_name =  'chrome phoobar' WHERE  product_name =  'Chrome Phoobar'; SELECT * FROM plsql!01_product WHERE  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, last_stock_date FROM  plsql!01_product WHERE  UPPER(product_name)  LIKE  '%PHOOBAR%';

Сравнив результаты с показанными на рис. 5.5, верните названия товаров к тому виду, который они имели до этого упражнения: UPDATE plsqll01_product SET  product_name  =  'Chrome  Phoobar' WHERE  product_name  =  'chrome  phoobar';

LENGTH

Временами требуется определять длину данных, хранимых в столбце таблицы.  Такую  возможность  предоставляет  функция  LENGTH.  Представьте,  например,  что  вы  используете  в  своей  работе  таблицу,  похожую  на  таблицу PLSQL10 l_PRODUCT, и названия товаров из нее поступают в отдел каталогов. Там  рассматривают  возможность  уменьшения  размера  страницы  каталога, а новый размер требует сокращения длины названий товаров с 25 до 15 символов. Чтобы принять окончательное решение, в отделе хотели бы знать, сколько названий придется изменить.  Вы можете составить список названий, требующих изменения, при помощи следующей команды: SELECT  product_name,  LENGTH(product_name)  NAME_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  104  Bottle,  Large NY  101  Box,  Snail NV  162  Box,  Large NY  103  Shipping  Carton,  Snail NV  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  6 

1 )

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



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

Г 

I

) +2,  99 ) FROM  plsqll01_old_item;

'

В данном случае длина подстроки не играет роли, поскольку вы извлекаете весь текст, оставшийся в строке. Чтобы подчеркнуть этот факт, я указал в функции SUBSTR длину 99. Теперь,  когда вы знаете,  как вычленять категорию и размер каждого изделия, пора объединить оба действия в одной команде.  Приведенный ниже код содержит две функции SUBSTR: одну для категории, а другую для размера. Для большей наглядности я поместил псевдоним столбца после каждой  функции SUBSTR, чтобы показать, какие атрибуты они извлекают. SELECT item_desc, ќSUBSTR(item_desc, 1, INSTR(item_desc, I  I /  t

1 )  -1 )  CATEGORY, SUBSTR(item_desc, INSTR(item_desc, t  I 

I t

1 )  +2,

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,

d

5 6  7  8 



1 ) -1 )  CATEGORY,

SU8STR(item desc,

10  11

IHSTR(item desc.

12 

1

13  14  15 

) +2, 99 )  ITEM SIZE

.

16  FROM  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

Shipping Carton, Large 

Shipping Carton

8  rows  selected.

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

'

| |

RTRIM(item_id) ||

2 3 Ч 5 6  FROM

и

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

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

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

NV-101  is described as  a Box, Snail. NV-102 is described as  a Box, Large. NV-103 is described as  a Shipping Carton, Small. NV-1B4 is described  as  a Shipping Carton,  Large.

8 rows selected. SQL>

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

136 

Глава 5

File  Edit  Seated  Option:  Help Item  ID  sentence

The  Iten ID for The  Iten ID for The  Iten ID for The  Iten ID for The  Iten  ID for The  Iten ID for The  Iten ID For The  Iten  ID for

Snail Can  is:  1.Й-101. Large  Can  is:  Ld-102. Snail  Bottle  is:  Lft-183. Large  Bottle  is:  LA-181». Small Box is: NY-101.

Large Box  is:  NV-102. Snail  Shipping  Carton  is:  NV-1Q3. Large  Shipping  Carton  is:  HY-104.

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 ADD_MONTHS('28-NOV-00',  1) FROM DUAL; 1 SELECT ADD_MONTHS('29-NOV-OO ,  1)  FROM DUAL; SELECT ADD_MONTHS('30-NOV-00', 1) FROM DUAL; SELECT ADD_MONTHS('31-DEC-00', -1)  FROM DUAL;

Обратите  внимание,  что  в  последней  команде  месяц  вычитается  из  даты "31 декабря". Формально ответом будет "31 ноября", но в ноябре только 30 дней. Функция ADD_MONTHS это знает и учитывает при  выдаче окончательного результата. Такая корректировка выполняется только для последнего дня месяца.  Как  видно  по  трем  предыдущим  командам,  в  остальных  случаях ADD_MONTHS добавляет ровно один месяц. Ж  Oiacle  SQL-Plus fife £dk !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 LAST_DAY(SYSDATE)  FROM DUAL; SELECT LAST_DAY('01-JAN-02')  FROM DUAL; SELECT LAST~DAY('15-JAN-02')  FROM DUAL; SELECT LAST_DAY('31-JAN-02')  FROM DUAL;

Функция  этого  типа  применяется  в  целом  ряде  ситуаций.  Например,  во многих компаниях медицинская страховка нового служащего начинает действовать с первого дня месяца,  следующего за месяцем приема на работу.  Если, скажем,  один человек начинает работать с  1  апреля,  а другой —  с  30 апреля, страховки обоих будут действовать с  1  мая. Теперь подумайте: как модифицировать функцию, возвращающую последний день месяца, чтобы она возвращала  первый день следующего  месяца? Ответ  прост:  нужно  добавить  1  к  значению,  возвращаемому  функцией LAST_DAY. Чтобы увидеть,  как это происходит на практике, создайте новую таблицу, содержащую данные о людях с датами их приема на работу. Это можно сделать при помощи следующих команд: CREATE TABLE plsql!01_person  ( person_code VARCHAR2(3), first_name  VARCHAR2(15), last_name  VARCHAR2(20), hiredate  DATE

INSERT INTO plsql!01_person VALUES CCA',  'Charlene1,  'Atlas',  'Ol-FEB-02'); INSERT INTO plsql!01_person VALUES ('GA',  'Gary',  'Anderson',  '15-FEB-02'); INSERT INTO plsql!01_person VALUES ( 'BB', 'Bobby' , 'Barkenhagen1;, '28-FEB-02 ')  ; INSERT INTO plsql!01_person VALUES ('LB',  'Laren',  'Baxter',  'Ol-MAR-02');

Глава 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

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,  'MONTH  DDTH 1 ) FROM DUAL;

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

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

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

144 

Глава5

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

Значение



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

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

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

AM  А.М.

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

ВС  Р В.L».

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

СС  SCC 

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



День недели (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

.  -



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

Ml  . 

Минуты (0-59)

М 

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

MON 

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

MONTH  РМ  P.M.

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

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



Квартал  года (1,2,3,4; январь-март =  1)

RM 

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

RR 

Для года, записанного двумя цифрами,  возвращает год следующего века, если год = 50; возвращает год предыдущего века, если год >= 50 и две последние цифры текущего года = 50 и две последние цифры текущего года   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  15-JAN-83 1888  15-JAN-82 100  15-JAN-83 18888 1234  15-JAN-84 1  89-JUL-88 V

6  rows  selected. SQL> •



лП

iLJ

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

ДИДДН  I' 'I v|

• *.  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

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

Глава5

И RES r

;;; 



'

2  product_price. 3  quantity_on hand, it  TO CHARUast stock date,  'MM-DD-YVVV НН24:МГ)  "Last  Stocked" 5  FROM  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  01-15-2003  BO:08 1B00  01-15-2802  00:00 100  01-15-2003  00:00 10000 .  1234  01-15-2004  00:00 1  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  01-15-2003  1000  01-15-2002  100  01-15-2003  10000 1234  01-15-2004  1  12-31-2002 

00:00 00:00 00:00 00:00 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 B ox, 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 после других языков.  Однако чисто визуально  "НЕМ" не привлекает внимание столь же легко,  как "--". Возможно,  именно поэтому вариант  "—" более распространен.

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

Глава 5

160 *  Шлг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

PRODUCT_PRICE  QUflHTITV_OH_HflHD  Lfl$T_STOC

Snail Widget  Medium Uodget  Chrome Phoobar  Round Chrome  Snaphoo  Extra Huge Mega Phoobar  +  Square  Zinculator  б  rows selected. 

99 75 50 25 9.95 45

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  14-JUL-B3 СЙ 75  14-JUL-83  BB 2  14-JUL-03  Gfi 8  15-JUL-B3  Gfl 26  15-JUL-Q3  LB 5  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 COUNT(1)  FROM plsqll01_product; SELECT COUNT(product_name) FROM plsql!01_product;  ' SELECT COUNT(last_stock_date)  FROM plsqll01_product; SELECT COUNT(last_stock_date)  / COUNT(product_name) "Populated Records" FROM  plsql!01_product;

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 1J»-JUL-03 С А 75 Ut-JUL-03  BB 2 U»-JUL-03 GA 8  1S-JUL-B3 GA 20 15-JUL-83 LB 5 16-JUL-03 CA

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

SUM(QUANTITV)

2

Chrome  Phoobar Medium  Uodget Round  Snaphoo Small  Widget

95 5 9

SQL>

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

••••' • •

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

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

Table  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 (



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 ; Blu • e 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

A A

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, 



plsql101_person.last_nari)e. 



plsql1B1_person.First_name,



plsql101~purchase. quantity

5  FROM 



i  7  ;

л.



plsql101_purchase,

plsq!101 person

PRODUCT_NAME

LflSTJMME

FIRST_NflME

Small Widget Medium Wodget Chrone Phoobar Small Widget

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

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

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

Small Widget

Baxter

Medium Wodget Round Chrome Snaphoo

Baxter Baxter Baxter Morton

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 Chrome  Phoobar Small Widget Medium Wodget Round Chrome Snaphoo Snail  Widget

i

ќ^

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, что не каждой записи главной таблицы может соответствовать запись в подчиненной таблице. Это делается путем помещения символа (+) после имени подчиненной таблицы в конструкции W HERE. В итоге команда принимает следующий вид: 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> SELECT product_nane FROM plsq!101_purchase 2  UNION 3  SELECT product_nane FROM plsql181_purchase_archiue 4  ORDER BV product_nane; PRODUCT NOME Chrome Phoobar Large HarFlinger Medium  Uodget Round Chrome Snaphoo Round  Snaphoo Small  Widget б  rows  selected. SQL>

-iLJ

Рис. 6.22. Оператор соединения UNION

UNION ALL Оператор  соединения  UNION  ALL  функционально  похож  на  оператор UNION, но он возвращает все возможные строки, а не только по одной строке для каждого уникального значения. Чтобы увидеть, в чем состоит отличие, введите  следующую команду: SELECT product_name  FROM plsql!01_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  plsqllOi_purchase WHERE  (pro'duct_name,  purchase_date) IN  (SELECT product_name,  MAX(purchase_date) FROM  plsql!01_purchase GROUP BY product_name

Итоги Эта глава основательно подготовила вас к работе с индексами и ограничениями в Oracle. Индексы баз данных похожи на книжные индексы: они содержат ключевую  информацию  из  всех  строк  таблицы  вместе  с  указателями  на  эти строки,  что позволяет намного быстрее находить строку с заданным значением — если, конечно, оно присутствует в индексе. После того как вы создали индекс,  Oracle  автоматически  поддерживает  его  синхронизацию  с  таблицей. Любые операции  INSERT,  UPDATE или  DELETE над этой таблицей будут автоматически изменять ее индекс, и любая команда SELECT будет выполняться с  привлечением  индекса,  если  он  содержит требуемые  столбцы.  Добавление или удаление индексов не влияет на операции с таблицей — любая использовавшая ее программа по-прежнему будет работать, хотя и более медленно. Если удалить таблицу, все ассоциированные с ней индексы также будут удалены, поскольку в отсутствие таблицы индекс бесполезен. Индексы  уменьшают  время  отклика  команд,  выполнение  которых  требует считывания содержимого таблицы. Добавление индексов к таблице не ускоряет выполнение команд INSERT; реально ввод данных в индексированные таблицы  замедляется,  поскольку  данные  необходимо  вставлять  также  и  в индекс(ы). Таким образом, использование индексов — это компромисс. Они замедляют ввод данных, но ускоряют их считывание. Самые распространенные типы индексов — это индексы В*-дерева и битовые индексы. Индексы В*-дерева используются в Oracle по умолчанию. Они подходят для столбцов,  содержащих большое количество уникальных значений — например, имен, личных идентификаторов или телефонных номеров. Битовые индексы, напротив, более пригодны для столбцов, содержащих малое количество уникальных значений — например, пол или значения типа да/нет. Для столбцов с низкой кардинальностью битовые индексы оказываются быстрее  индексов  В*-дерева. Когда наступает время задуматься о качестве хранимых данных, вы можете использовать ограничения, чтобы обеспечить соответствие данных некоторым минимальным требованиям. Создавая ограничение, вы определяете одно или несколько  условий,  которым должны  удовлетворять  введенные  пользователем значения.  Ограничения хранятся как часть определения таблицы,  а после создания  применяются  автоматически.  Когда  введенная  кем-либо  команда

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

217

INSERT или UPDATE нарушает ограничение, Oracle прерывает ее выполнение, производит откат и выдает сообщение об ошибке. Ограничение может быть столь же простым, как и требование, чтобы столбец содержал данные — неважно какие. Оно может гарантировать, что все значения в столбце будут уникальны, тем самым исключая дублирование записей. Оно даже позволяет проверять вводимые значения и  принимать только те из них, которые удовлетворяют заданному вами условию. Это может пригодиться для исключения таких случайностей,  как отрицательные цены товаров, даты транзакций, относящиеся к позапрошлому веку или несуществующие штаты. Последний пример указывает на возможность определять ограничения, в которых значения, вставляемые в одну таблицу, сравниваются со значениями, уже содержащимися в другой таблице, что позволяет успешно избегать таких проблем, как выполнение транзакций по продаже несуществующих товаров. Связанные данные, хранящиеся в разных таблицах, часто требуется соединять и представлять в одном списке. Это можно делать в операторах SELECT, включая в них конструкцию WHERE с указанием первичного ключа (уникально  идентифицирующего столбец (столбцы)  главной таблицы и соответствующего ему внешнего ключа подчиненной таблицы.

Вопросы 1.  Что из перечисленного не относится к достоинствам индексов? A. Ускоренное выполнение команд INSERT B. Ускоренное выполнение команд UPDATE C. Ускоренное выполнение команд SELECT D. Ускоренное выполнение команд DELETE 2.  На какой строке будет выдано сообщение об ошибке при выполнении этой команды? CREATE  INDEX  plsqll01_purchase_pk  ON  plsqll01_purchase  ( product_name, salesperson, purchase_date ); A. 1 B-2 

,

с. з D.4

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

218 

Глава 6 /

B. В*-дерево C. Битовый D. Другой 4.  Какая из перечисленных команд позволила бы гарантировать, что товар, название которого вводится в запись о покупке, существует в таблице товаров? A.  CREATE  INDEX имя_индекса ON  имя_таблицы(имя_столбца)\ B.  ALTER TABLE имя_таблицы  MODIFY (имя_столбца NOT NULL); C.  ALTER TABLE имя_таблицы ADD  CONSTRAINT имя_огрантения UNIQUE  (имя_столбца); D.  ALTER TABLE имя_таблицы ADD CONSTRAINT имя_ограничения CHECK/имя_столбца  условие,); E. ALTER TABLE имя_таблицы ADD CONSTRAINT имя ограничения FOREIGN KEY (имя_столбца)  REFERENCES имя__главной таблицы; 5.  Что будет выведено в результате выполнения приведенной ниже команды, если таблица 1 содержит пять записей, а таблица 2 — десять записей? SELECT  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 TABLE plsql!01_purchase; DROP TABLE plsql!01_product; DROP TABLE plsql!01_person; DROP TABLE plsqll01_old_item; DROP TABLE plsqll01_purchase_archive; ' CREATE TABLE plsql!01_persbn  ( person_,code  VARCHAR2O)  PRIMARY' KEY, first_name  VARCHAR2(15), last_name  VARCHAR2(20), hire_date  DATE

) ;

ќ  ќ 

CREATE  INDEX plsql!01_person_name_index ON plsql!01_person(last_name,  first_name); ALTER TABLE plsql!01_person ADD CONSTRAINT plsql!01_person_unique UNIQUE ( first_name, last name, hire_date

» r

INSERT  INTO plsql!01_person VALUES CCA',  'Charlene', 'Atlas', 'Ol-FEB-02') ; INSERT INTO plsql!01_person VALUES ('GA', 'Gary',  'Anderson', '15-FEB-02') ; INSERT INTO plsql!01_person VALUES ('BB',  'Bobby',  'Barkenhagen',  '28-FEB-02'); INSERT INTO plsql!01_person VALUES ('LB',  'Laren',  'Baxter',  'Ol-MAR-02'); INSERT INTO plsql!01_person VALUES  ( 'LN',  'Linda1,  'Norton',  'Ol-JUN-03'); CREATE TABLE plsqll01_product  ( product_name  VARCHAR2(25)  PRIMARY KEY, product_price  NUMBER(4,2), quantity_on_hand  NUMBER(5,0),

.  ќ   : 

ќ 

-

Другие полезные средства Oracle  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 с подзапросом, извлекающим вставляемые данные из другой таблицы. Чтобы  создать  таблицу  назначения  для  демонстрации  этой  техники,  введите следующую команду: Л;ќ 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 Charlene  75  Bobby  2  Gary  8  Gary  28  Laren  5  Charlene  1  Charlene 

7  rows  selected. SQL>

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

Й В и Й В Й и

Глава?

228

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

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

ИИ  El

file £dil Se«ch Uptons НФ  SQL> CREATE TABLE plsqll 01_purchase_log2 AS  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

QUANTITY FIRST_NAME

1 charlene

51

75 Bobby 2 Gary

99 75 25 99

20 Laren S Gharlene 1 Charlene

8 Gary

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

Глава 7

232

file  Edit  Search  Options  Help   > 1 SQL>  UPDATE  plsql101_log  SET  data_load_date  - 15-ОЕС-2вВ3

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 OR REPLACE VIEW plsql!01_sales_by_atlas_v AS SELECT * FROM  plsql!01_purchase WHEJ^E  salesperson  =  'CA'

SELECT * FROM plsqllOl sales by atlas v; _  _ j_  _

Глава 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> CREATE OR REPLACE VIEW plsqll 01 sales by atlas и AS 2  SELECT * Э  FROM  plsqll 01_purchase u  WHERE  salesperson -  *CA' 5 ; 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  TABLE plsqllOljourchase; DROP TABLE plsql!01_product; DROP TABLE plsqllOljoerson; DROP TABLE plsqll01_old_item; DROP  TABLE plsql!01_purchase_archive; CREATE TABLE plsql!01_person ( person_code  VARCHAR2P)  PRIMARY KEY, first_name  VARCHAR2 (15)  , las t_name  VARCHAR2 (20), hire_date  DATE

CREATE  INDEX plsqll01_person_name_index ON plsql!01_person(last_name, first_name)  ; ALTER TABLE plsql!01_person ADD CONSTRAINT plsql!01_person_unique UNIQUE  ( first_name,

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

253

last_name, hiredate

INSERT  INTO plsql!01_person VALUES CCA',  'Charlene',  'Atlas',  'Ol-FEB-02'); INSERT  INTO plsql!01_person VALUES 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

и

BEGIN

5 б 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 = 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

|x

-l]

3  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 = 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 10 11 12 .13

-

tiny  NUMBER  :=  20; med NUMBER  := 600; big NUMBER := 4550; wrong  NUMBER  :=  -35; dbms_output.put_line ('  Order dbms_output.put_line (tiny  ||  ќ dbms_output.put_line (med |j  ' dbms_output.put_line (big  j| ' dbns output.put_line (wrong ||

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

END; /

Order

AND

Discount

20  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

just_a_num NUMBER  := 1; 3  BEGIN i» 5 6 7 8 9  END; 10  /

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

WHERE ORDER  BV

SflL 

QUflNTITV  PRODUCT_PRICE

BB Си Си Си GA GA LB

SELECT

FROM

75 1 5 1 8 2 28

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

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  ' I I  Num_b); EXCEPTION WHEN ZERO_DIVIDE THEN DECLARE err_num .NUMBER  :=  SQLCODE; err_msg VARCHAR2(512)  := SQLERRM; BEGtN dbms_output.put_line('ORA Error Number ' || err_nuia ) ; dbms_.output.put_line('ORA Error message  '  ||  err_msg) ; dbms_output.put_line(' Value of Num_a  '  || Num_a); dbms_output.put_line(' Value of Num_b  ' || Num_b); END; END;

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

285

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

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

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

CURSOR.ALREADYOPEN

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

DUPVAL.ONJNDEX

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

INVAUD.CURSOR * ' • • ' . ' 

•  '•  '•  .'• 

:  • 



Попытка применить команду 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

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

Глава 8

286 Я  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 /

||  nun b);

WHEN ZERO D I U I D E THEN DECLARE

err nun NUMBER  ;= SQLCODE; err msg  UflRCHHR2(512)  := SQLERRM; BEGIN dbns output. put_line('ORn Error Number ' |  | err_nun  ) ; dbms_output.put_line( 'ORft Error message ' I I  err_«sg); dbms output .put line( '  Ualue of  num__a  ' || num_a); dbms output. put line(' Ualue of nun b ' 1 1 num_b) ; END;

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); —  ==================  Снижение  цен  (курсор  for  update)  === DECLARE

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^:- 

.  . 

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

THEN 

,:^.  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   I I one_perform.person_name  I  I I   '  I I  , one_perform.current_sales  I I I

I

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

END; / SELECT * FROM plsql!01_person; SELECT * FROM plsqll01_purchase; SELECT * FROM plsql!01_purchase_archive; SELECT * FROM plsqll01_product;

Давайте посмотрим, что происходит в этом сценарии. В самом начале объявлен  тип  записи  с  именем  PERFORMANCEJTYPE.  Добавление  %TYPE  к именам полей, извлекаемых из таблицы PLSQL101_PERSON, гарантирует, что типы данных в  таблице и  процедуре всегда будут совпадать, даже  если табличные типы данных в будущем изменятся! Затем объявляется переменная типа записи с именем ONE_PERFORM, а следом за ней — простой курсор, который выбирает все строки из таблицы PLSQL101_PERSON. Процедура CURRENT_PERFORMANCE принимает два параметра. Один параметр — это запись о продавце, тип которой совпадает с типом строки таблицы PLSQL101_PERSON, а также с типом записей, выбираемых ранее объявленным  курсором  PERSON_CUR.  Таким  образом,  этой  процедуре  можно безопасно передавать записи  из  курсора PERSON_CUR.  ;  ; Второй  параметр,  A_PERFORM,  объявлен  как  OUT,  и  это  означает,  что процедура  будет  вести  в  него  запись.  Процедура  содержит  явный  курсор HISTORY_CUR,  соединяющий  таблицы  PLSQL101_PRODUCT  и PLSQL101_PURCHASE_ARCHIVE,  чтобы  найти  среднюю  сумму  заказа для данного  продавца.  Это  архивированные  (исторические)  данные,  отсюда  имя HISTORY_CUR.  На  основе  этого  значения  будет  определяться,  насколько успешно  продавец  выполнял свои текущие  продажи.  HIST_RBC—  это  переменная  типа  записи,  основанная  на  курсоре  HISTORY_CUR. CURRENT_AVG_SALES  —  среднее  значение  по  всем  текущим  заказам продавца. Выполняемая секция процедуры начинается с кода, который копирует фамилию продавца и его личный код в выходной параметр A_PERFORM. Затем статусу  присваивается  начальное  значение  NULL. 11  Зак.  725

Глава 9

304 1 ќ Я-  ni.-irlr- Ч1Л 4'liK 

File £dil

Search

НИЕЗ

Options

Help

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

-d

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

ќ ќ ќ ќ ; . . ' 

PER  FIHSTJffiME 

LftST_NftME 

HIRE_DflTE

Си  GA  BB  LB  LN 

Atlas  Anderson  Barkenhagen  Baxter  Norton 

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

Charlene  Gary  Bobby  Laren  Linda 

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.

1 ;...ќќ QUANTITY

CA  14-JUL-B3  BB  14-JUL-B3  GA  11-JUL-B3  GA  15-JUL-B3  LB  15-JUL-83  CA  16-JUL-B3  CA  17-JUI-B3  , 

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

ќ ќ ' ќ ќ '

,

SQL> SELECT » FROM plsqll 81_purchase_archiue; PRODUCT_NAME 

SAL PURCHASE. 

QUANTITY

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

BB  21-JUN-B1  GA  22-JUN-S1  LB  23-JUN-B1  ZZ  24-JUN-B2  CA  2S-JUN-B2  JT  26-JUN-B2 

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

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

^

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

305

Далее создается неявный курсор, крторый мы подробнее рассмотрим в следующем разделе. В данном случае неявный курсор'подсчитывает общий объем текущих продаж и текущую среднюю сумму заказа для продавца, данные о котором получены из входного параметра A_PERSON. Эта часть кода расположена  в  своем  собственном  базовом  блоке,  чтобы  можно  было  перехватывать исключения, возбуждаемые только этим кодом. Как видите, исключение было возбуждено для Линды Нортон. У нее отсутствуют текущие продажи, поэтому соответствующие данные не были найдены. Затем курсор  HISTORY_CUR открывается и  из  него  выбираются данные. Заметьте, что произведена единственная выборка, поскольку для одного продавца может существовать только одна сводная запись. Обратите также внимание на использование псевдонима столбца AVG_ORDER. Курсор принимает личный код продавца в качестве параметра, поэтому код курсора может многократно использоваться для разных продавцов. В архиве может не окажется данных для указанного продавца, если он был только что принят на работу. Могут возникнуть и другие ошибки, в результате которых курсор не вернет ни одной записи. В нашем случае таблица PLSQL101_PRODUCT не содержит данных о двух  товарах  (Large  Harflinger  и  Round  Snaphoo).  Вот  почему  сообщение  об ошибке выглядит как "erroneous or no history", а не просто "no history". Если найдены верные исторические данные, мы вычисляем процентное отношение текущей средней суммы заказа к ее историческому значению и помещаем его в поле A_PERFORM.PERFORM_PERCENT. В  заключение  курсор  HISTORY_CUR  закрывается.  Секция  исключений предназначена  для  перехвата  непредвиденных  исключений NO_DATA_FOUND, которые не были перехвачены ранее. Выполняемая секция главного анонимного блока циклически выбирает записи из курсора PERSON_CUR, вызывает для каждой записи процедуру и распечатывает результаты. Заключительные операторы  SELECT позволяют убедиться,  что  программа делает именно то, для чего она предназначалась, и определить, в чем состояли причины возникших ошибок. Этот законченный пример охватывает много нового материала. Впоследствии он будет модифицирован и использован для иллюстрирования некоторых других тем этой главы. Обязательно разберитесь в этом примере, чтобы подготовить себя к дальнейшему чтению.  . , .  -  ,  .,.,..;•

DML в PL/SQL, или неявные курсоры В этом разделе мы подробно рассмотрим неявные курсоры, которые были использованы выше. Взгляните на последний пример. Оператор, определяющий текущую среднюю сумму заказа^ представляет собой обычный оператор SELECT, дополненный ключевым словом INTO. Это ключевое слово требуется для того, чтобы поместить возвращённые оператором значения в соответствующие переменные PL/SQL. В нашем примере этими переменными являются CURRENT_AVG_SALES и A_PERFORM.CURRENT_SALES. Мы можем использовать данный оператор SELECT только при условии, что он возвращает не более одной записи.  Если он вернет более одной записи, будет возбуждено "исключение TOO_MANY_ROWS. Для этого оператора PL/SQLиспользует не-

п*

Глава 9 яйный курсор, называемый "SQL".  Курсор имеет атрибуты, к которым можно обратиться для получения информации о последней выполненной SQL-операции.  Например,  атрибут SQL%FOUND сообщит,  выбрал ли последний оператор  SELECT  какие-нибудь  записи.  При  наличии  двух  последовательных операторов SELECT информация в SQL%FOUND будет относиться только ко второму из них. Давайте  устраним  причины  исключений,  возбуждавшихся  в  предыдущем примере. Сначала вставим данные о продажах для Линды, использовав оператор  INSERT  в  коде  PL/SQL.  Сравните  свои  результаты  с  показанными  на рис. 9.2.

НП-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, 1 18-flUG-02 • , 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  НИНЕ 

SftL  PURCHftSE_ 

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

Си  BB  Си  Gfl  LB  Си  СЙ  LN 

QUftNTITV

14-JUL-03  14-JUL-03  14-JUL-03  15-JUL-03  15-JUL-B3  16-JUL-03  17-JUL-03  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 

prod rec plsql101  product%ROWTVPE ) IS

НВЕЭЦД



л —

4  BEGIN

5  6 

UPDATE plsq!181_product SET  last_stock_date  » prod_rec.last_stock  date, 7  quantity_on_hand  -  quantity_on_hand . . 8. . ' ' ќ  ' +  9  prod_rec.quantity on_ham| 10  WHERE product_name - prod_rec.product_name; 11  END update prod;

. .

ќ  ; -

I  12  /

ќ

i Procedure  created. ќ SQL>  DECLARE



a plsq!181  product%ROWTVPE; ^ '  ' 

3  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 Й -

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 



!ќќ

:'

I

SQL> SELECT « FROM plsq!101_product; PRODUCT_NAME 

ќ

' ,  .

ќ 

/' 

96.03 72.75 48.5 24.25 8.69 39.29 42.78 1.71 1.71 21 12

ќ  ќ  !

32 23-NOU-01 1000 15-  JAN-  82 100 15-JAN-03 18000 1234 15-JAN-04 1 31  -DEC-  02

5 10  31-DEC-01 10 30-DEC-01 100 29-AUG-01 144 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

• ' • , • " • '  - 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'  || ' ' I I '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   ' I I 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 one_perforn. status) ; END  LOOP; dbns_output.put line('Pkg load date seconds  '  ||

42 TO_CHftR ( p lsqii 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  Performance^ Errors? СЙ Atlas 313.31 0 7.7 All  fine Gfl Anderson 865.24 8.6524 -58.8 All fine BB Barkenhagen 5456.25 272.8125 4446.9 All fine LB Baxter 1455 72.75 All fine LN Norton 72.75 Erroneous or no history 1455 Pkg load date  seconds  39571 System date seconds 39571 PL/SQL procedure successfully completed. SQL>

-iLJ 

-

Рис. 9.9. Пример пакета и его использования

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

321

Обратите внимание,  что для курсора PKG_PER_CUR требуется указывать тип возвращаемого значения.  Переменная CHAR  имеет исключительно "косметическое" назначение: она улучшает вид распечатки. Я скрыл коэффициенты скидок внутри пакета, чтобы его пользователи не могли их модифицировать. DATE_LOADED — это переменная пакета. На некоторых компьютерах код выполняется настолько быстро, что между моментом  загрузки  пакета  и  текущим  системным  временем  нет  никакой  разницы. Однако вы заметите разницу, если выполнение замедлится или хост-компьютер базы данных будет иметь меньшую производительность.

Триггеры Вы уже познакомились с триггерами в главе 8.  Сначала мы повторим этот материал, а затем углубимся в подробности. Триггер лучше всего описать как процедуру, которая автоматически выполняется  при  возникновении  некоторого  события,  указанного  в  определении триггера, — триггерного события (triggering event). Вы можете писать триггеры, срабатывающие в одной из следующих ситуаций: •  Применение оператора DML к определенному объекту схемы •  Выполнение оператора DDL внутри схемы или базы данных •  Вход пользователя в систему или выход его из системы, ошибка сервера, запуск базы данных или останов экземпляра Между триггерами и процедурами PL/SQL есть три различия: •  Триггеры нельзя вызывать из кода программы. Oracle вызывает их автоматически в ответ на определенное событие. •  Триггеры не имеют списка параметров. •  Спецификация триггера немного отличается от спецификации процедуры. Сходство между триггерами и процедурами состоит в следующем: •  Тело триггера выглядит точно так же, как и тело процедуры — и то, и другое суть базовые блоки PL/SQL. •  Триггер не возвращает никаких значений. •  Триггеры можно использовать для  выполнения разнообразных задач. •  Триггеры автоматически генерируют производные значения столбцов. Например, при обработке каждого нового заказа комиссионные торгового агента могут рассчитываться и вводиться в соответствующую таблицу автоматически. •  Триггеры помогают предотвращать неверные транзакции.  Например, триггер может воспрепятствовать увеличению зарплаты служащего на большую величину, чем предусмотрено бюджетом. Заметьте, что это нельзя сделать с помощью контрольных ограничений, поскольку 12*

322 

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

•  Триггеры можно использовать для реализации сложных процедур авторизации. Например, значение зарплаты могут изменять только руководители и только для людей, находящихся в их подчинении. •  Триггеры  используются  для  реализации  сложных  бизнес-правил. Например, триггер может проверять, доступен ли заказанный товар к дате отгрузки, и если нет, то автоматически размещать заказ на пополнение запасов, чтобы на момент отгрузки на складе находилось достаточное  количество  этого  товара. •  Триггеры могут обеспечивать прозрачное протоколирование событий, например, отслеживать, сколько раз отдельный продавец обращался к таблице с инвентарной ведомостью. •  Триггеры могут обеспечивать сложный аудит.  Например, с помощью триггера легко выяснить, какое среднее число обращений к базе данных требуется данному продавцу для обработки одного заказа. •  Триггеры могут собирать статистику доступа к таблицам.  Например, триггер позволяет зафиксировать, сколько раз в течение дня выполнялись запросы на "Small Widget" к таблице с каталогом товаров. Как вы уже, вероятно, поняли, триггеры представляют собой весьма мощные  инструменты.  Однако их следует использовать аккуратно,  поскольку триггер  может  срабатывать  при  каждом  обращении  к  таблице,  тем  самым увеличивая нагрузку на сервер базы данных. Разумное практическое правило состоит в том, чтобы использовать триггер только для действий, которые нельзя  выполнить другими средствами.  Например,  если  есть возможность создать одно или несколько ограничений,  выполняющих работу некоторого триггера, следует  использовать  эти  ограничения.  Если  итоговые  величины  допустимо вычислять в нерабочее время, систему лучше спроектировать именно так, а не использовать  триггеры,  срабатывающие  при  каждой  транзакции.  Предположим, например, что банк принимает решение о доставке дополнительной наличности  из  центрального  хранилища  в  зависимости  от  того,  сколько 20-долларовых банкнот было  выдано  за  текущий день.  Эту  величину  можно определять  по  окончании  рабочего  дня.  Если  же  для  обновления  счетчика 20-долларовых банкнот будет использоваться триггер, то его постоянное срабатывание может существенно замедлить скорость выполнения транзакций в рабочие часы. Триггеры,  определенные для  таблиц,  называются  табличными  триггерами (table triggers). Далее речь пойдет именно о них.  Синтаксис создания триггера имеет следующей вид: CREATE  OR  REPLACE  ТШССЕКимя_триггерамомент_срабатывания триггерное_событие ON  имя_таблицы [WHEN  триггерное_ргрантение] [FOR EACH ROW]

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

323

[DECLARE объявления] BEGIN операторы [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 

USER  MHCHINE 

12  LN  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 

USER_MACHINE 

12  LN  PLSQL1B1  13  LB  PLSQL101  1*  LB  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 

LN  PLSQL1B1  LB  PLSQL1B1  LB  PLSQL1B1  BB  PLSQL1B1 

USER_HACHINE 

CHAHGE_IN_QUANT  TRANSACTI

ALPHANERD  ALPHANERD  ALPHANERD  ALPHANERD 

2  22-OCT-BB 8  22-OCT-B8 8  22-OCT-D8 -55  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  LN  13  LB  14 LB  15 BB 

PLSQL1B1 PLSQL181 PLSQL1B1 PLSQL1B1

USER_MACHINE  ALPHANERD ALPHANERD ALPHANERD ALPHANERD

CHAHGE_IN_QUANT  TRANSACTI 2 22-ОСТ-ев 8 22-OCT-BB 8 22-OCT-BB -55 22-OCT-88

SQL>

Рис. 9.10.  Триггер, запрещающий изменение названия товара при обновлении заказов

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

327

Давайте поговорим о том, что здесь происходит.  Сначала к таблице покупок добавляется столбец ORDER_NUMBER. Затем создается таблица для аудита записей, содержащая имя вошедшего в систему пользователя. Имя пользователя вводится с помощью функции USER,  а имя его компьютера или терминала — с помощью функции USERENV, вызываемой с параметром TERMINAL1. Также вводятся личный код продавца, изменение в количестве заказанного товара, номер заказа и дата изменения. Триггер не позволяет изменятьproduct_name и автоматически генерирует номера для новых заказов, а также старых записей в таблице заказов, которые не были пронумерованы. При успешном обновлении или  вставке  триггер  вставляет  значения  в  таблицу  аудита.  Ключевые  слова UPDATING и  INSERTING сообщают нам, что послужило запускающим действием — обновление или вставка. Сначала  триггер  был  протестирован  путем  вставки  строки  в  таблицу PLSQL101_PURCHASEn  сравнения старых значений с новыми.  Затем были выполнены два  простых обновления,  а  в заключение  —  попытка изменить  название товара в заказе.

Модификация триггеров Для триггеров, как и для представлений, не существует команды модификации. Старый триггер просто заменяется новым с помощью команды 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. 



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П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  ИФ

v м | »*

PRODUCTJIAHE  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? Available  tabtes  and  columns: 6!  PLSQL101 .PRODUCT 

Columns  in your  query: 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 

• 

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  '||H

_J 

Al 

1  1  2  3  A  5  6  7  8  9 

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 



SAL CA BB GA GA LB CA CA LN



С 



D  J_



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 



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