1
Федеральное агентство по образованию Национальный исследовательский ядерный университет ―МИФИ‖
В.С. Лаврентьев
Осво...
143 downloads
584 Views
2MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
1
Федеральное агентство по образованию Национальный исследовательский ядерный университет ―МИФИ‖
В.С. Лаврентьев
Освоение SQL и PL/SQL Oracle Лабораторные работы Учебное пособие
Москва 2009
2
УДК 681.3.06 ББК 32.973.2 Л 57 Лаврентьев В.С. [Электронный ресурс] Освоение SQL и PL/SQL Oracle. Лабораторные работы: учебное пособие.- М.:МИФИ, 2009.-105с. Данное учебное пособие не является учебником по SQL и PL/SQL Oracle. Цель пособия – представить студентам образец выполнения лабораторных работ с тем, чтобы в соответствии с этим образцом они выполняли лабораторные работы на выдаваемой преподавателем другой предметной области, для каждого студента группы – отдельной, не совпадающей с другими. Запросы и другие команды SQL скомпонованы в шесть тем, каждая из которых соответствует одной лабораторной работе. Образец выполнения лабораторных работ по освоению SQL представлен для предметной области торговой фирмы. В качестве образца для выполнения лабораторных работ по освоению PL/SQL использованы прямые ссылки на предметную область и скрипты из книги Скотта Урмана «Oracle8. Программирование на языке PL/SQL (М.: "Лори", 1999)», на которые и делается ссылка в пособии. В соответствии с компоновкой языковых конструкций PL/SQL в названной книге определены и темы восьми лабораторных работ по освоению PL/SQL. Рецензент канд. техн. наук, доц. Т.В.Петрова Рекомендовано редсоветом МИФИ к изданию в качестве учебного пособия
ISBN 978-5-7262-1100-8 © Национальный иссследовательский ядерный университет «МИФИ», 2009 Редактор Е.Е.Шумакова Прикладное программное обеспечение для выполнения лабораторных работ: Oracle® Database Express Edition 10g Release 2 (10.2) для Microsoft Windows. Системные требования: PC не ниже класса Pentium IV минимум 256 мегабайт RAM, рекомендуется 512 мегабайт; свободное место на HDD 1,6 гигабайт, Windows 2000 Service Pack 4 или более поздняя, Windows Server 2003, Windows XP Professional Service Pack 1 или более поздняя; дисковод CD-ROM 2-х и выше, мышь, клавиатура. Уч.-изд л. 13.0. Изд №048-1. Формат 60Х84 1/8. Национальный исследовательский ядерный университет «МИФИ», 115409, Москва, Каширское ш., д.31
3
Оглавление Предисловие….…………………………………………………………………………………4 Глава I. Общая последовательность этапов выполнения лабораторных работ по освоению языка SQL Oracle………………………………………………………………………………..5 Глава II. Образец для выполнения лабораторных работ по освоению SQL Oracle………..6 II.1. Утилита SQL*Plus, ее настройка и работа с ней при выполнении простых запросов выборки столбцов и строк………………………………………………….……6 II.1.1. Команды утилиты SQL*Plus и ее настройка…………….………………………6 II.1.2. Создание пользователя Oracle и таблиц его схемы……………………………..8 II.1.3. Переменные привязки в среде SQL*Plus……………...………………………..10 II.1.4. Переменные подстановки в среде SQL*Plus……………………....…………...11 II.1.5. Выборка столбцов таблицы………………….…………………………………..14 II.1.6. Выборка строк таблицы…………………………….…………………………....17 II.2. Выборка данных из нескольких таблиц…………………………………………….20 II.2.1. Подзапросы……………………………………………………………………….20 II.2.2. Коррелированные подзапросы……………………………………….…………..23 II.2.3. Использование подзапросов во фразе FROM……………………..….................25 II.2.4. Операторы над множествами……………………………………………………26 II.2.5. Объединения (внутренние)………………………………………………………27 II.2.6. Внешние объединения………………………..…………………………………..30 II.2.7. Иерархические запросы…………………………………………………………..32 II.3. Функции Oracle SQL…………………………………………………..…………........35 II.3.1. Некоторые символьные функции……………………………………………….35 II.3.2. Некоторые функции даты и времени…………………………………………...39 II.3.3. Некоторые функции преобразования…………………………………………..41 II.3.4. Функции DECODE, DUMP, ORA_HASH, VSIZE……………………………..46 II.3.5. Агрегатные и аналитические функции…………………………………………49 II.4. Другие команды языка манипулирования данными DМL и обработка транзакций…………………………………………………………………………………...55 II.4.1. Команды DML insert, update, delete……………………………………………...55 II.4.2. Обработка транзакций……………………………………………………………60 II.5. Создание и изменение структуры таблиц средствами DDL………………………...62 II.6. Другие объекты базы данных…………………………………………………………67 Глава III. Общая последовательность этапов выполнения лабораторных работ по освоению языка PL/SQL Oracle…………………………………………………..……………72 Библиографический список…………….……………………………………………………...75 Приложение 1. Скрипт demo.sql создания предметной области пользователя DEMO …..76 Приложение 2. Содержимое таблиц пользователя DEMO...………………………………..88 Приложение 3. Решения отдельных задач SQL олимпиады 2007г.………………………..99
4
Предисловие Учебные курсы «Базы данных» и «Безопасность систем баз данных I» в качестве практической базы для проведения лабораторных работ по освоению SQL и процедурного языка СУБД предполагают использование программного продукта корпорации Oracle – Oracle10g EE. Названный программный продукт получен МИФИ участником программы «ORACLE ACADEMY ADVANCED COMPUTER SCIENCE &BUSINESS». В соответствии с договором между МИФИ и корпорацией Oracle, каждый студент кафедр 29 и 43, выполняющий лабораторные работы по названным выше учебным курсам, может установить на домашнем компьютере Oracle10g EE, полученный МИФИ от Oracle (взяв у преподавателя соответствующую DVD копию), но при этом от студента требуется регистрация на сайте http://oai.oracle.com/pls/oai/sel onchange.main?cmd=s SET lang. Студенты, не желающие регистрироваться, для выполнения лабораторных работ могут воспользоваться установкой разрешенной к практически неограниченному использованию версией Oracle10g Express Edition, скачав ее с сайта www.oracle.com. Следует подчеркнуть важность установки студентом СУБД Oracle на домашнем компьютере для обеспечения возможности самостоятельной работы. Необходимость самостоятельной работы объясняется насыщенностью того материала, который должен осваивать студент на занятиях в дисплейном классе. Хорошо известен афоризм выдающегося физика Фейнмана: «..понять, значит – привыкнуть и научиться использовать». Времени занятий в дисплейном классе, конечно, не хватает для того, чтобы «привыкнуть» к SQL и PL/SQL Oracle и уж тем более научиться их использовать. Пожалуй, основная сложность, которую приходится преодолевать при освоении технологий современной СУБД, такой, как Oracle – гигантская размерность этого программного продукта, интегрирующего практически все современные информационные технологии. Предлагаемые в настоящем учебно-практическом издании лабораторные работы при их осмысленном выполнении, в сочетании с лекционным материалом, обучение отдельным технологиям Oracle в следующем семестре (уже после освоения SQL и PL/SQL) постепенно, шаг за шагом вводят студента в эту гигантскую размерность с тем, чтобы далее студент мог двигаться в интересующем его направлении уже в значительной степени самостоятельно. Задания на лабораторные работы по освоению PL/SQL формулируются с использованием прямых ссылок на /2/, имеющуюся в библиотеке МИФИ. Программные скрипты из /2/, по образцу которых студенты должны выполнять задания, в свое время были выложены на сайте издательства этой книги и поэтому имеются в распоряжении студентов. Что касается основной по объему части учебного пособия, представляющей образец выполнения лабораторных работ по SQL такой книги, как /2/, с готовыми к использованию скриптами по всем SQL лабораторным работам, т.е. такими скриптами, которые студенту не надо самому набирать на клавиатуре компьютера, в распоряжении студентов не имеется. Поэтому автор подготовил в электронном виде образец выполнения лабораторных работ по освоению SQL. При подготовке образца автором использована общая последовательность подачи материала, семантика многих запросов SQL, представленные в /1/, с корректировками на другие таблицы измененной предметной области. По сравнению с /1/ существенно расширен раздел «Функции SQL», добавлены типы запросов, учитывающие отдельные аспекты развития SQL в последующих версиях Oracle со ссылками на /3/,/4/.
5
Глава I. Общая последовательность этапов выполнения лабораторных работ по освоению языка SQL 1. Получение от преподавателя схемы (Entity Relationship диаграммы) и скриптов (на языке SQL) индивидуальной предметной области для выполнения лабораторных работ. 2. Ознакомление с примерами выполнения SQL запросов по шести разным темам на предметной области пользователя DEMO. Примеры выполнения запросов по этим шести темам представлены в главе 2. «Образец для выполнения лабораторных работ по освоению SQL Oracle» настоящего учебного пособия. Содержание тем: Тема 1. Утилита SQL*Plus, ее настройка и работа с ней при выполнении простых запросов выборки столбцов и строк. Тема 1 включает раздел по созданию пользователя DEMO, наделению его необходимыми полномочиями и созданию таблиц предметной области. Тема 2. Выборка данных из нескольких таблиц. Тема 3. Функции Oracle SQL. Тема 4. Другие команды языка манипулирования данными DМL и обработка транзакций. Тема 5. Создание и изменение таблиц средствами DDL. Тема 6. Другие объекты базы данных. 3. Выполнение на заданной преподавателем по п.1 предметной области SQL запросов по шести приведенным в п.2. темам. Запросы должны быть такого же типа и в таком же количестве, как и в главе 2. «Образец для выполнения лабораторных работ по освоению SQL Oracle» настоящего учебного пособия. Запросы должны предваряться их формулировкой на естественном языке. При выполнении лабораторных работ студенту может потребоваться выполнить корректировку «своей» предметной области (добавить столбец в таблицу, определить его внешним ключем, заполнить его; добавить новую таблицу и заполнить ее; заполнить новыми данными ранее существовавшую таблицу и т.д.). Такая корректировка может потребоваться, например, для демонстрации на своей предметной области иерархических запросов, запросов с использованием агрегированных функций и т.д. Все эти корректировки должны быть скомпонованы в отдельном скрипте, который, как и spool –файлы (протоколы выполнения) лабораторных работ, предъявляются преподавателю при сдаче лабораторных работ. Лабораторные работы могут выполняться студентом вне дисплейного класса, а на занятиях – предъявляться преподавателю для их сдачи. Лабораторные работы по освоению SQL завершаются в первой половине семестра написанием контрольной работы «SQL запросы» (на седьмой – восьмой неделях семестра). Во второй половине семестра выполняются лабораторные работы, связанные с освоением процедурного языка программирования Oracle PL/SQL. Вторая половина семестра также завершается контрольной работой «Хранимые процедуры и триггера на языке PL/SQL». Программами учебных курсов «Базы данных» и «Безопасность систем баз данных I» предусмотрено 2 часа аудиторных лабораторных занятий в неделю – для каждого студента. В ходе лабораторной работы на начальном ее этапе определенное время тратится на запуск приложений Oracle, подготовку студентом изменений в базе данных, необходимых для демонстрации преподавателю выполненных лабораторных работ (создание пользователя, наделение его необходимыми привилегиями, создание объектов схемы этого пользователя, выполнение скриптов задания). Чтобы уменьшить относительный вес этих накладных расходов, график выполнения лабораторных работ по названным учебным курсам предусматривает длительность аудиторной лабораторной работы для каждого студента четыре академических часа раз в две недели. Поэтому на каждом из первых трех занятий (шесть первых недель семестра) студент должен сдавать преподавателю лабораторные работы по двум темам, чтобы к контрольной работе по SQL выполнить все шесть тем.
6
Глава II. Образец для выполнения лабораторных работ по освоению SQL Oracle II.1. Утилита SQL*Plus, ее настройка и работа с ней при выполнении простых запросов выборки столбцов и строк II.1. 1. Команды утилиты SQL*Plus и ее настройка SQL*Plus представляет собой инструмент, с помощью которого можно интерактивно или в пакетном режиме определять данные, управлять ими в базах данных Oracle, выполнять команды SQL, PL/SQL программы. Алфавитный список команд SQL*Plus включает до 50 команд (file:……doc/server.102/b14356/toc.htm#CFAJHBCH)1). В этом разделе мы приведем минимум команд SQL*Plus, необходимых для выполнения лабораторных работ. В табл. II.1 приведены наиболее часто используемые команды SQL*Plus. Таблица II.1 Команда Назначение SPOOL путь и имя файла SPOOL OFF|ON
Инициализирует запись протокола работы SQL*Plus в текстовый файл, расположенный по указанному пути
COL[UMN] название_столбца FORMAT 9--9.9--9
При выводе столбца числового типа с указанным именем выводимый размер примет значение стольких разрядов целой части, сколько девяток поставлено перед точкой, с добавлением стольких разрядов дробной части, сколько девяток было поставлено после точки
ED|IT
Вызов редактора, в котором можно править последнюю команду в буфере SQL*Plus Выход из SQL*Plus Устанавливает длину выводимой строки в n символов Устанавливает размер выводимой страницы, равным n строк Обеспечивает возможность работы пакета (программного модуля) DBMS_OUTPUT, обеспечивающего вывод информации при работе программ PL/SQL Выводит подсказку «текст»
OFF ION отключает/возобновляет запись протокола работы SQL*Plus в текстовый файл DESC[RIBE] имя таблицы Выводит в окне SQL*Plus структуру таблицы Выполняет команду операционной системы из SQL*Plus HOST команда ОС CONNECT имя/пароль Соединение с сервером Oracle под другим именем/паролем COL[UMN] При выводе столбца символьного типа с указанным именем его название_столбца выводимый размер примет значение n символов FORMAT An
EXIT SET LINESIZE N SET PAGESIZE N SET SERVEROUTPUT ON
SET SQLPROMPT ‗текст‘
SET ECHO {ON | OFF}
Устанавливает или прекращает вывод в окне SQL*Plus выполняемых команд в sql скриптах
SET TERM[OUT] {ON | OFF}
Устанавливает или прекращает вывод в окне SQL*Plus результатов выполнения команд в sql скриптах
SAVE название
Запоминание скрипта последнего запроса в файле «название скрипта»
скрипта @название SQL скрипта
Запустить на выполнение SQL скрипт
1)
Эту строку надо вставлять, как URL, в браузере, указав путь к документации Oracle10g на своем компьютере»
7
Команды в SQL*Plus пишутся сразу после выводимой подсказки (сразу после установки – это «SQL>»). Регистр написания команд SQL*Plus не важен. Отметим здесь, что команды языка SQL и команды SQL*Plus – не одно и то же. В отдельных частях команд SQL, там, где задаются значения строк или столбцов хранимых таблиц, регистр важен. Команды SQL, вводимые в окне SQL*Plus, могут занимать несколько строк, заканчиваются точкой с запятой и выполняются после нажатия клавиши «Enter». Другой способ выполнения: на последней строке SQL команды ввести символ «/» и нажать клавишу «Enter». Последняя введенная команда хранится в буфере SQL*Plus. SQL*Plus можно запускать в интерфейсе Windows (D:\oracle\product\10.2.0\db_1\BIN\ sqlplusw.exe)1), а также – в окне DOS (D:\oracle\product\10.2.0\db_1\BIN\ sqlplus.exe). Ссылка на SQL*Plus в интерфейсе Windows есть в панели задач отдельной строкой, откуда его обычно и запускают. Для запуска SQL*Plus в окне DOS достаточно выполнить в нем команду sqlplus без указания пути. Для получения читаемых сообщений на кириллице в свойствах окна DOS надо установить шрифт TT Lucida Console и изменить кодовую страницу командой «chcp 1251» или шрифт в свойствах окна DOS надо оставить без изменения (точечный), кодовую страницу установить командой «chcp 866» и затем выполнить команду «set nls_lang= RUSSIAN_CIS.RU8PC866». Названные настройки «срабатывают» для русскоязычной версии Windows. Зачастую при работе с SQL*Plus важно видеть, какой пользователь работает с базой данных и с какой базой данных он работает. У автора настоящего пособия на компьютере постоянно находится несколько баз данных с разными версиями Oracle (Oracle9i, Oracle10g Enteprise EditionE, Oracle10g Express Edition). Для того чтобы иметь ответы на эти вопросы, достаточно изменить подсказку по умолчанию в окне SQL*Plus (SQL>) на подсказку из двух частей: имени пользователя и имени базы, разделенных знаком, например , «@». Способ такой настройки указан в первом томе книги Тома Кайта «Oracle для профессионалов». Этот способ был предложен Томом Кайтом на момент его работы с Oracle9i. В версии Oracle10g настройка существенно упростилась по сравнению с предложенной Кайтом. Параметры SQL*Plus определены в скрипте glogin.sql. Этот скрипт расположен в директории D:\oracle\product\10.2.0\db_1\sqlplus\admin1. Каждый раз при соединении пользователя с сервером Oracle посредством SQL*Plus автоматически запускается скрипт glogin.sql. В этот скрипт glogin.sql надо добавить строку: SET SQLPROMPT "_USER'@'_CONNECT_IDENTIFIER>" Здесь _USER и _CONNECT_IDENTIFIER предопределенные (predefined) переменные SQL*Plus Release 10.2, первая из которых определяет текущего пользователя, вторая – идентификатор соединения, который обычно совпадает с первой частью (в нашем случае ORCL) глобального имени базы данных. Файл glogin.sql приобретает такой, например, вид: SET SERVEROUTPUT ON size 1000000 -- последняя команда включает по умолчанию поддержку --пакета DBMS_OUTPUT (чтобы не нужно было набирать эту команду каждый раз); --устанавливает также максимально возможный размер буфера, используемого --при работе этого пакета SET TRIMSPOOL ON --последняя команда гарантирует, что в выдаваемом тексте хвостовые пробелы --будут отсекаться. Если используется стандартное значение этой установки (OFF), --выдаваемые строки будут иметь длину LINESIZE 1)
Здесь и далее указанный путь предполагает, что при установке Oracle строка «Oracle Home» имеет значение «D:\oracle\product\10.2.0\db_1\».
8
SET LONG 5000 --последняя команда устанавливает количество байт, выдаваемых при выводе столбцов --типа LONG и CLOB SET LINESIZE 200 -- последняя команда устанавливает размер выводимой строки SET PAGESIZE 9999 --последняя команда устанавливает, что названия столбцов будут повторяться только --после вывода указанного числа строк SET SQLPROMPT "_USER'@'_CONNECT_IDENTIFIER>" --нижеприводимые комментарии к командам и сами команды (за исключением последних --двух) служат для форматирования столбцов и представлены в «родном» glogin.sql, -- появляющемся при установке Oracle10g -- Used by Trusted Oracle COLUMN ROWLABEL FORMAT A15 -- Used for the SHOW ERRORS command COLUMN LINE/COL FORMAT A8 COLUMN ERROR FORMAT A65 WORD_WRAPPED -- Used for the SHOW SGA command COLUMN name_col_plus_show_sga FORMAT a24 COLUMN units_col_plus_show_sga FORMAT a15 -- Defaults for SHOW PARAMETERS COLUMN name_col_plus_show_param FORMAT a36 HEADING NAME COLUMN value_col_plus_show_param FORMAT a30 HEADING VALUE -- Defaults for SHOW RECYCLEBIN COLUMN origname_plus_show_recyc FORMAT a16 HEADING 'ORIGINAL NAME' COLUMN objectname_plus_show_recyc FORMAT a30 HEADING 'RECYCLEBIN NAME' COLUMN objtype_plus_show_recyc FORMAT a12 HEADING 'OBJECT TYPE' COLUMN droptime_plus_show_recyc FORMAT a19 HEADING 'DROP TIME' -- Defaults for SET AUTOTRACE EXPLAIN report -- These column definitions are only used when SQL*Plus -- is connected to Oracle 9.2 or earlier. COLUMN id_plus_exp FORMAT 990 HEADING i COLUMN parent_id_plus_exp FORMAT 990 HEADING p COLUMN plan_plus_exp FORMAT a60 COLUMN object_node_plus_exp FORMAT a8 COLUMN other_tag_plus_exp FORMAT a29 COLUMN other_plus_exp FORMAT a44 -- Default for XQUERY COLUMN result_plus_xquery HEADING 'Result Sequence' SET echo on SET termout on II.1. 2. Создание пользователя Oracle и таблиц его схемы Большинство примеров по освоению SQL запросов в настоящем пособии «привязаны» к незначительно скорректированной предметной области, поставлявшейся в прежней версии Oracle. ERD диаграмма этой предметной области представлена на рис.1. Корректировка заключается в сокращении размера нескольких записей в таблице «customer», сокращении размера трех столбцов и удалении одного столбца этой таблицы с целью обеспечения более удобного для восприятия вывода информации из этой таблицы. Добавлено также по паре строк в таблицы department b location. Скрипты создания и наполнения таблиц пользователя DEMO представлены в приложении 1.
9
CUSTOMER # CUSTOMER_ID o NAME o ADDRESS o CITY o STATE o ZIP_CODE o AREA_CODE o PHONE_NUMBER * SALESPERSON_ID FK o CREDIT_LIMIT
EMPLOYEE # EMPLOYEE_ID o LAST_NAME o FIRST_NAME o MIDDLE_INITIAL o JOB_ID FK o MANAGER_ID o HIRE_DATE o SALARY o COMMISSION * DEPARTMENT_ID FK
JOB # JOB_ID * FUNCTION
DEPARTMENT # DEPARTMENT_ID o NAME o LOCATION_ID FK
SALES_ORDER
LOCATION
# ORDER_ID o ORDER_DATE * CUSTOMER_ID FK o SHIP_DATE o TOTAL
# LOCATION_ID o REGIONAL_GROUP
ITEM
SALARY_GRADE # GRADE_ID LOWER_BOUND UPPER_BOUND
# ITEM_ID # ORDER_ID FK o PRODUCT_ID FK o ACTUAL_PRICE o QUANTITY o TOTAL
PRODUCT # PRODUCT_ID o DESCRIPTION
PRICE # PRODUCT_ID # START_DATE o LIST_PRICE o MIN_PRICE o END_DATE
Рис.II.1 Server Model Diagram предметной области, используемой в этом учебном пособии для освоения SQL
FK
10
Создание предметной области состоит из нескольких шагов: a. Создание пользователя. b. Назначение привилегий пользователю. с. Соединение созданного пользователя с Oracle и запуск на исполнение скрипта, создающего и заполняющего таблицы предметной области. Для создания пользователя DEMO надо «войти» в SQL*Plus пользователем SYSTEM. По умолчанию при установке Oracle этот пользователь имеет пароль manager (отметим здесь, что регистрозависимый пароль «появился» только в Oracle11g, поэтому пароль, как и имя пользователя, можно вводить на любом регистре). Подсказка «system@10g» появится, если предварительно создать и разместить в вышеуказанном месте %ORACLE_HOME%\sqlplus\admin файл glogin.sql в соответствии с п. II.1.1. SYSTEM@ORCL> CREATE USER demo IDENTIFIED BY demo DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp QUOTA 1M ON users; Пользователь создан. SYSTEM@ORCL> GRANT CONNECT, RESOURCE, CREATE VIEW TO demo; Привилегии предоставлены. Эта команда позволит пользователю DEMO соединяться с сервером Oracle и создавать объекты (таблицы, программные объекты, представления и т.д.) базы данных. SYSTEM@ORCL> CONNECT demo/demo Соединено. Таков будет результат выполнения этой команды, если предварительно выполнена настройка утилиты SQL*Plus в соответствии с пунктом II.1. 1. Пользователь DEMO теперь должен запустить скрипт создания и заполнения таблиц. Чтобы такой скрипт «появился», скопируйте текст приложения 1 в блокнот (notepad.exe) и разместите его в папке c:\tmp, например, с именем demo.sql. После этого выполните команду: SYSTEM@ORCL> @c:\tmp\demo.sql II.1.3. Переменные привязки в среде SQL*Plus В SQL*Plus можно также выделять именованную область памяти для хранения некоторой информации. Такая область используется внутри программ PL/SQL и SQL операторов, однако находится вне программных блоков, поэтому можно по очереди выделять ее разным программным блокам и после выполнения каждого из них выводить ее содержимое. Эта именованная область памяти называется переменной привязки bind variable /1,2/. Переменная привязки (в приведенном ниже примере «var1») в SQL*Plus создается с помощью команды VARIABLE. Переменная привязки может быть использована внутри программы PL/SQL и при использовании в ней пишется, предваряясь двоеточием. Q1_1 (Query 1 по теме 1). Создание, обращение к и вывод переменной привязки. DEMO@ORCL>VARIABLE var1 CHAR(10) DEMO@ORCL>BEGIN :var1:='Oracle'; END; / Процедура PL/SQL успешно завершена.
11
DEMO@ORCL>PRINT var1 VAR1 --------Oracle Во второй части курса в следующем семестре на конкретном примере мы увидим, что использование переменных привязки в работе программных кодов с повторяющимися циклами способствует эффективному выполнению этих кодов. II.1.4. Переменные подстановки в среде SQL*Plus Q1_2 (Query 2 по теме 1). Использование переменной подстановки («t_name» в данном случае) для имени таблицы и значения столбца («n_loc» в данном случае) location_id в ней при извлечении информации. Знак «&» предшествует имени переменной подстановки. DEMO@ORCL>SELECT * FROM &t_name WHERE location_id=&n_loc; Введите значение для t_name: location Введите значение для n_loc: 123 прежний 1: SELECT * FROM &t_name WHERE location_id=&n_loc новый 1: SELECT * FROM location WHERE location_id=123 LOCATION_ID REGIONAL_GROUP -----------------------------123 CHICAGO Q1_3. Использование переменной подстановки («1» в данном случае) для значения столбца location_id. DEMO@ORCL>SELECT REGIONAL_GROUP FROM location WHERE location_id ='&1'; Введите значение для 1: 124 прежний 2: WHERE location_id ='&1' новый 2: WHERE location_id ='124' REGIONAL_GROUP ----------------DALLAS Сохраним последний запрос в файл Q1_3.sql DEMO@ORCL>SAVE Q1_3 Создано file Q3_1.sql Посмотрим значение параметра SQL*Plus termout DEMO@ORCL>SHOW TERMOUT termout ON Посмотрим значение параметра SQL*Plus ECHO DEMO@ORCL>SHOW ECHO echo ON
12
Выполним команды «SET ECHO OFF», «SET TERMOUT OFF». Выполним скрипт Q1_3 с вводимым значением столбца location_id =124 DEMO@ORCL>@Q1_3 124 DEMO@ORCL> Скрипт выполнен, но ни выполняемая команда SQL, ни результат выполнения этой команды SQL*Plus не вывел. Задействуем параметр TERMOUT и выполним скрипт Q1_3 с вводимым значением столбца location_id =124 еще раз DEMO@ORCL>SET TERMOUT ON DEMO@ORCL>@Q1_3 124 прежний 2: WHERE location_id ='&1' новый 2: WHERE location_id ='124' REGIONAL_GROUP -------------------DALLAS Выведен результат выполнения SQL команды, но сама команда не выведена. Задействуем теперь параметр ECHO и выполним скрипт Q1_3 с вводимым значением столбца location_id =124 еще раз DEMO@ORCL>SET ECHO ON DEMO@ORCL>@Q1_3 124 DEMO@ORCL>SELECT regional_group FROM location WHERE location_id ='&1' / прежний 2: WHERE location_id ='&1' новый 2: WHERE location_id ='124' REGIONAL_GROUP -------------------DALLAS Выведены SQL команда и результат ее выполнения. Отметим здесь, что при отключенном параметре TERMOUT вывод полностью отключается вне зависимости от значения параметра echo DEMO@ORCL>SET TERMOUT OFF DEMO@ORCL>@Q1_3 124 DEMO@ORCL> Не выводится не только результат выполнения команды, но и сама команда, не смотря на установленный в «ON» параметр echo. Вернем прежнее значение параметра, выполнив «SET TERMOUT ON». Q1_4. Использование переменных подстановки («loc_number», «reg_group» в данном случае) для вставки значений двух столбцов location_id, regional_group в таблицу location. DEMO@ORCL>INSERT INTO location(location_id, regional_group) VALUES(&loc_number, ®_group); Введите значение для loc_number: 125 Введите значение для reg_group: 'МОСКВА'
13
прежний 1: INSERT INTO location(location_id, regional_group) VALUES(&loc_number,®_group) новый 1: INSERT INTO location(location_id, regional_group) VALUES(125,'МОСКВА') 1 строка создана. !!!Для возврата содержимого таблицы к исходному состоянию ликвидируем последнюю вставку командой «ROLLBACK» DEMO@ORCL>ROLLBACK; откат завершен. Q1_5. Использование двойного знака «&&» для переменной подстановки («loc» в данном случае). Значение введенной после сдвоенного знака «&» переменной подстановки запоминается в SQL*Plus, и в следующий раз при обращении к этой переменной подстановки ее значение вводится автоматически. DEMO@ORCL>SELECT * FROM location WHERE location_id=&&loc ; Введите значение для loc: 123 прежний 1: SELECT * FROM location WHERE location_id=&&loc новый 1: SELECT * FROM location WHERE location_id=123 LOCATION_ID REGIONAL_GROUP -----------------------------123 CHICAGO
DEMO@10g>SELECT * FROM department WHERE location_id=&&loc; прежний 1: SELECT * FROM department WHERE location_id=&&loc новый 1: SELECT * FROM department WHERE location_id=123 DEPARTMENT_ID NAME LOCATION_ID -----------------------------------30 SALES 123 34 OPERATIONS 123 Здесь уже отсутствует приглашение для ввода значения «loc». Покажем теперь, как изменить значение для переменной подстановки «loc». Сначала выведем текущее значение этой переменной: DEMO@ORCL>DEFINE loc DEFINE LOC = "123" (CHAR) Теперь присвоим этой переменной подстановки «loc» новое значение: DEMO@ORCL>DEFINE loc = 124 DEMO@ORCL>SELECT * FROM department WHERE location_id=&&loc; прежний 1: SELECT * FROM department WHERE location_id=&&loc новый 1: SELECT * FROM department WHERE location_id=124 DEPARTMENT_ID NAME -------------------------20 RESEARCH 23 SALES 24 OPERATIONS
LOCATION_ID ----------124 124 124
Отменим присвоенное значение для переменной «loc»:
14
DEMO@ORCL>UNDEFINE loc DEMO@ORCL>SELECT * FROM department WHERE location_id=&&loc; Введите значение для loc: 122 прежний 1: SELECT * FROM department WHERE location_id=&&loc новый 1: SELECT * FROM department WHERE location_id=122 DEPARTMENT_ID NAME -------------------------10 ACCOUNTING 12 RESEARCH 13 SALES 14 OPERATIONS
LOCATION_ID ----------122 122 122 122
II.1.5. Выборка столбцов таблицы Q1_6. Выборка всех столбцов из таблицы DEMO@ORCL>SELECT * FROM department;
Q1_7. Использование арифметических выражений. Общая стоимость отдельных товаров в покупке DEMO@ORCL>SELECT order_id, actual_price*quantity FROM item; ORDER_ID ACTUAL_PRICE*QUANTITY -----------------------------600 42 610 58 611 45 - - - - - - - - - - - - - - 510 1252,8 510 83,8 271 строк выбрано. Q1_8. Интерпретация NULL-значений столбца. NULL значение в таблицах Oracle понимается, как не определенное, неизвестное значение, это, например, не число «0», если столбец имеет числовой тип, не строка из пробелов, если столбец имеет символьный тип.
15
Повторим сначала: DEMO@ORCL>SELECT * FROM department;
Для department_id 55 и 56 location_id не выводятся, так как для этих отделов в столбце «location_id» содержатся NULL значения. Явный вывод в числовом виде значений столбца «location_id» DEMO@ORCL>SELECT department_id, name, NVL(TO_CHAR(location_id),’нет’ ) AS location FROM department;
Функция TO_CHAR() преобразует числовой тип в символьный, NVL() с совместно используемой функцией TO_CHAR() возвращает символы «нет», если значение в столбце «location» NULL и значение этого столбца – в противном случае. Q1_9. Сцепление столбцов department_id и name DEMO@ORCL>SELECT location_id||' '||regional_group FROM location;
Q1_10. Использование литералов (набора символов) для формирования столбца DEMO@ORCL>SELECT ' Department ', name, 'settled in region', location_id FROM department;
16
Q1_11. Исключение повторяющихся строк с использованием DISTINCT DEMO@ORCL>SELECT DISTINCT name FROM department; NAME -------------ACCOUNTING OPERATIONS RESEARCH SALES Выведено 4 строки, хотя в таблице department 13 строк. Q1_12. Использование псевдонимов столбцов DEMO@ORCL>SELECT name AS НАЗВАНИЕ_ОТДЕЛА, location_id AS РАСПОЛОЖЕНИЕ FROM department;
Q1_13. Упорядочение строк результата запроса с использованием ORDER BY DEMO@ORCL>SELECT department_id, name FROM department ORDER BY name;
17
Или иначе: DEMO@ORCL>SELECT department_id, name FROM department ORDER BY 2;
Q1_14. Сортировка в порядке убывания DEMO@ORCL>SELECT department_id, name FROM department ORDER BY 2 DESC;
Q1_15. Сортировка по нескольким столбцам DEMO@ORCL>SELECT department_id, name FROM department ORDER BY 2,1 DESC;
II.1.6. Выборка строк таблицы Условия выборки во фразе WHERE Q1_16. Вывести имя, город и кредитный рейтинг всех покупателей из штата «NEW YORK» («NY»)
18
DEMO@ORCL>SELECT name, city, credit_limit FROM customer WHERE state='NY'
Q1_17. Выбор отделов, не прикрепленных ни к какому региону DEMO@ORCL>SELECT * FROM department WHERE location_id IS NULL;
Q1_18. Вывести отделы, не принадлежащие региону с номером 122 DEMO@ORCL>SELECT * FROM department WHERE NVL(location_id,0) !=122;
Q1_19. Вывести сведения о заказах, оплаченных в день заказа DEMO@ORCL>SELECT * FROM sales_order WHERE ship_date=order_date;
Q1_20. Выбор покупателей, у которых кредитный рейтинг меньше или равен 10000 и больше или равен 8000 DEMO@ORCL>SELECT name, credit_limit FROM customer WHERE credit_limit BETWEEN 8000 AND 10000;
19
Q1_21. Выбор отделов , номера регионов которых 123 или 124 DEMO@ORCL> SELECT * FROM department WHERE NVL(location_id,0) in (123,124);
Q1_22. Выбор имени, города и штата покупателей, у которых имя начинается на «A» DEMO@ORCL>SELECT name, city, state FROM customer WHERE name LIKE 'A%';
Q1_23. Выбор имени, города и штата покупателей у которых кредитный рейтинг не меньше 9000 DEMO@ORCL>SELECT name, city, state, credit_limit FROM customer WHERE NOT credit_limit < 9000;
Q1_24. Выбор из таблицы price товаров, закупленных фирмой в 1988 году, у которых определено поле end_date ( не имеет NULL значение). При отработке этого запроса используется усложненный критерий поиска с объединением AND DEMO@ORCL>SELECT * FROM price WHERE TO_CHAR(start_date,’YY’)=’88’ AND end_date IS NOT NULL;
20
Q1_25. Выбор из таблицы price товаров, закупленных фирмой в 1988 или 1989 годах, у которых не определено поле end_date (имеет NULL значение). При отработке этого запроса используется усложненный критерий поиска с объединением AND и OR DEMO@ORCL>SELECT * FROM price WHERE (TO_CHAR(start_date,’YY’)=’88’ OR TO_CHAR(start_date,’YY’)=’89’) AND end_date IS NULL;
Q1_26. Использование SELECT-а в SELECT-е или вложенный подзапрос в SELECT-е. Вывести номер, имя и название отдела служащего для служащих 20-го отдела DEMO@ORCL>SELECT employee_id, last_name , (SELECT name FROM department WHERE department.department_id=employee.department_id) AS dname FROM employee WHERE department_id=20;
II.2. Выборка данных из нескольких таблиц II.2.1. Подзапросы Q2_1. Найти покупателей , имеющих минимальный кредитный рейтинг DEMO@ORCL>SELECT name, city, state, credit_limit FROM customer WHERE credit_limit =(SELECT MIN(credit_limit) FROM customer);
В запросе использована агрегатная функция min(credit_limit), которая возвращает минимальное значение столбца «credit_limit». Q2_2. Найти покупателей из штата «NY» имеющих тот же кредитный рейтинг, что и покупатели из штата «TX», обслуживаемые служащим по фамилии «WEST» DEMO@ORCL>SELECT name, state, city, credit_limit FROM customer WHERE state ='NY' AND credit_limit in (SELECT credit_limit FROM customer WHERE state='TX' AND salesperson_id= (SELECT employee_id FROM employee
21
WHERE last_name='WEST'));
Q2_3. Найти покупателей, имеющих минимальный кредитный рейтинг в своем штате DEMO@ORCL> SELECT name, state, city, credit_limit FROM customer WHERE (state, credit_limit) IN ( SELECT state, MIN(credit_limit) FROM customer GROUP BY state) ORDER BY state;
Подзапрос «SELECT state, MIN(credit_limit) FROM customer GROUP BY state» рассмотрим отдельно, так как здесь впервые в приводимых примерах использована опция «GROUP BY». DEMO@ORCL> SELECT state, MIN(credit_limit) FROM customer GROUP BY state;
Казалось бы, добавить к перечню извлекаемых столбцов «name», и нужный результат будет получен DEMO@ORCL> SELECT name, state, MIN(credit_limit) FROM customer GROUP BY state; SELECT name, state, MIN(credit_limit) FROM customer GROUP BY state * ошибка в строке 1: ORA-00979: выражение не является выражением GROUP BY Но получено сообщение об ошибке, так как если требуется выводить минимум значения зарплаты по сгруппированным строкам, в опции GROUP BY надо указывать все столбцы (кроме столбца credit_limit, конечно, по которому ищется минимум), составляющие выводимые строки. Подкорректируем запрос с учетом полученной ошибки DEMO@ORCL> SELECT name, state, MIN(credit_limit) FROM customer GROUP BY state, name;
22
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Покупатели в штатах разные, поэтому мы получили 33 группы строк по одной строке в каждой из них. Для одной строки и выводится минимум, совпадающий со значением credit_limit в этой строке. Изменим значение столбца «name», установив в нем одинаковое значение для всех 33-х строк DEMO@ORCL>UPDATE customer SET name='TYMOTY_SPORT'; и повторим предыдущий запрос SELECT name, state, MIN(credit_limit) FROM customer GROUP BY state, name;
Вот теперь, так как фамилии в отделах одинаковые, запрос извлекает пять групп одинаковых строк, «принадлежащих» каждая одному штату. И уже внутри каждой из таких групп строк ищется MIN(credit_limit). !!!Вернем столбцу name в таблице EMPLOYEE прежние значения DEMO@ORCL>ROLLBACK; Q2_4. Получить перечень покупателей штата «NY», имеющих кредитный рейтинг больше минимального в штата «TX» DEMO@ORCL> SELECT name, state, city, credit_limit FROM customer WHERE state ='NY' AND credit_limit > ANY(SELECT credit_limit FROM customer WHERE state='TX');
Часть запроса « … credit_limit > ANY()…» означает условие: кредитный рейтинг больше любого значения в скобках. Другой вариант отработки этого запроса: DEMO@ORCL> SELECT name, state, city, credit_limit FROM customer
23
WHERE state ='NY' AND credit_limit > (SELECT MIN(credit_limit) FROM customer WHERE state='TX'); Q2_5. Получить перечень покупателей штата «NY», имеющих кредитный рейтинг больше любого в штате «MA» DEMO@ORCL> SELECT name, state, city, credit_limit FROM customer WHERE state ='NY' AND credit_limit > ALL (SELECT credit_limit FROM customer WHERE state='MA');
Часть запроса « … credit_limit > ALL()…» означает условие: кредитный рейтинг больше всех значений в скобках. Q2_6. Определить штаты, в которых число покупателей больше одного и средний кредитный рейтинг больше среднего кредитного рейтинга по штату «MA» DEMO@ORCL> SELECT state, AVG(credit_limit) FROM customer GROUP BY state HAVING COUNT(name)>1 AND AVG(credit_limit)> (SELECT AVG(credit_limit) FROM customer WHERE state='MA');
Здесь использованы агрегатные функции AVG() и COUNT(), а также – опция HAVING, смысл которой понятен из текста запроса. Q2_7. Определить год, когда в компании было продано товара на самую большую сумму DEMO@ORCL>SELECT TO_CHAR(ship_date, 'YYYY') AS year, SUM(total) AS amount_of_sale FROM sales_order GROUP BY TO_CHAR(ship_date, 'YYYY') HAVING SUM(total)=(SELECT MAX(SUM(total)) FROM sales_order GROUP BY TO_CHAR(ship_date, 'YYYY'));
II.2.2. Коррелированные подзапросы Q2_8. Определить заказчиков, кредитный рейтинг которых превышает средний кредитный рейтинг в штате (state), где они находятся DEMO@ORCL>SELECT customer_id, name, state, credit_limit FROM customer X WHERE credit_limit > (SELECT AVG(credit_limit) FROM customer Y
24
WHERE Y.state=X.state) ORDER BY state, customer_id;
В запросе используется таблица customer с алиасом «X» и копия этой таблицы. Последовательность действий Oracle при отработке запроса такова: извлекается первая строка из таблицы с алиасом «X», из этой строки извлекается аббревиатура штата, для этого штата в копии таблицы подсчитывается среднее значение кредитного рейтинга, и если кредитный рейтинг в извлеченной строке оказывается выше подсчитанного среднего, соответсвующие столбцы из извлеченной строки «укладываются» в результат, вслед за тем извлекается вторая строка из таблицы с алиасом «X» и т.д. – в цикле. Q2_9. Получить список покупателей, не проживающих ни в одном из городов, в которых находятся отделы фирмы DEMO@ORCL> SELECT customer_id,name,city "Город_закакзчика" FROM customer X WHERE NOT EXISTS ( SELECT 1 FROM location WHERE regional_group=X.city);
25
Этот же вывод можно попытаться получить и без корреляционного запроса DEMO@ORCL>SELECT customer_id,name,city "Город_закакзчика" FROM customer X WHERE city NOT IN ( SELECT regional_group FROM location); строки не выбраны Запрос отработан неверно, так как одно из значений внутреннего запроса – NULL значение, отчего отработка условия «…city NOT IN (SELECT regional_group FROM location)» ничего не извлекает. Это касается только сравнения «NOT IN». Для «IN» запрос отработает верно и выдаст такой же результат, как и запрос Q2_10 (см. ниже). Правильно для «NOT IN» надо сделать так: DEMO@ORCL>SELECT customer_id,name,city "Город_закакзчика" FROM customer X WHERE city NOT IN ( SELECT NVL(regional_group, ' ') FROM location); Q2_10. Получить список покупателей, проживающих в одном из городов, в которых находятся отделы фирмы DEMO@ORCL> SELECT customer_id,name,city "Город_закакзчика" FROM customer X WHERE EXISTS ( SELECT 1 FROM location WHERE regional_group=X.city);
Q2_11. Коррелированный подзапрос при использовании оператора UPDATE. Создадим таблицу employee1: DEMO@ORCL>CREATE TABLE employee1 AS SELECT * FROM employee; Таблица создана. Добавим столбец к таблице employee1: DEMO@ORCL>ALTER TABLE employee1 ADD(dname VARCHAR2(14)); Таблица изменена. А теперь заполним значениями новую колонку: DEMO@ORCL>UPDATE employee1 X SET dname=(SELECT name FROM department WHERE department_id=X.department_id); 32 строк обновлено. DEMO@ORCL>DROP TABLE employee1; Таблица удалена. II.2.3. Использование подзапросов во фразе FROM Q2_12. Сформировать отчет: год продаж, относительная (в процентах) доля денежных средств, получаемых фирмой от продаж в этом году по отношению к общей сумме
26
продаж за все годы, относительная (в процентах) доля количества покупок в этом году по отношению к общему количеству покупок за все годы DEMO@ORCL>SELECT sum_year.year , ROUND(100* sum_year.sm_year /sum_total.ss_total,2) AS "% Many" , ROUND(100* count_year.cnt_year / count_all_years.cnt_all_years,2) AS "% Purchase " FROM (SELECT TO_CHAR(ship_date,'YYYY') AS year, SUM(total) AS sm_year FROM sales_order GROUP BY TO_CHAR(ship_date,'YYYY') ) sum_year , (SELECT SUM(total) AS ss_total FROM sales_order) sum_total , (SELECT TO_CHAR(ship_date,'YYYY') AS year, COUNT(order_id) AS cnt_year FROM sales_order GROUP BY TO_CHAR(ship_date,'YYYY')) count_year , (SELECT COUNT(order_id) AS cnt_all_years FROM sales_order) count_all_years WHERE sum_year.year= count_year.year;
II.2.4. Операторы над множествами Q2_13. Пересечение множеств INTERSECT. Определить отделы с одинаковым названием, размещенные в регионах 122, 123 DEMO@ORCL>SELECT name FROM department WHERE location_id=122 INTERSECT SELECT name FROM department WHERE location_id=123; NAME -------------OPERATIONS SALES
Q2_14. Объединение множеств UNION. Определить названия отделов, размещенных в регионах 122 или 123 DEMO@ORCL>SELECT name FROM department WHERE location_id=122 UNION SELECT name FROM department WHERE location_id=123; NAME -----------ACCOUNTING OPERATIONS RESEARCH SALES
UNION ALL дубликаты не исключаются. DEMO@ORCL>SELECT name FROM department WHERE location_id=122 UNION ALL
27
SELECT name FROM department WHERE location_id=123; NAME -------------ACCOUNTING RESEARCH SALES OPERATIONS SALES OPERATIONS
6 строк выбрано Q2_15. Разность множеств MINUS Определить названия отделов, размещенных в регионе 122 но не 123 DEMO@ORCL>SELECT name FROM department WHERE location_id=122 MINUS SELECT name FROM department WHERE location_id=123; NAME ------------ACCOUNTING RESEARCH
II.2.5. Объединения (внутренние) При внутренних объединениях выводится информация из извлекаемых столбцов, присутствующая в таблицах, участвующих в запросе. В отличие от внутреннего, при внешнем объединении выводится частичная информация по запросу, даже если в одной из объединяемых таблиц информация отсутствует. Так, в нижеприводимом запросе Q2_16 выводится информация, связанная только с регионом 167, в котором существуют отделы, а информация о регионе 168, в котором отделов нет, не выводится. Внешнее объединение позволяет вывести информацию и о регионе 168, при этом столбцы «name», «region_group» будут не заполнены. Как выполнять внешние объединения, мы ознакомимся в разделе II.2.6. Q2_16. Соединение по равенству таблиц location, department по полю location_id DEMO@ORCL> SELECT department.name, location.location_id, location.regional_group FROM department , location WHERE department.location_id= location.location_id AND location.location_id IN (167, 168);
Q2_17. Использование псевдонимов имен таблиц DEMO@ORCL> SELECT d.name, l.location_id, l.regional_group FROM department d, location l WHERE d.location_id=l.location_id AND l.location_id IN (167, 168);
28
Q2_18. Соединения не по равенству /1/ Категория оплаты каждого сотрудника компании DEMO@ORCL>SELECT e.last_name, e.salary, s.grade_id FROM employee e, salary_grade s WHERE e.department_id=30 AND e.salary between s.lower_bound AND s.upper_bound;
Q2_19. Соединение таблицы самой с собой /1/ Определить служащих, зачисленных на работу раньше своих руководителей DEMO@ORCL>SELECT e.last_name AS name, e.hire_date AS "date", e.job_id AS "job" ,m.last_name AS "boss", m.hire_date AS "date" FROM employee e, employee m WHERE e.manager_id=m.employee_id AND e.hire_date < m.hire_date;
Q2_20. Определить служащих , имеющих оклад больше оклада своего руководителя /1/ DEMO@ORCL>SELECT e.last_name AS name, e.salary AS sal_emp ,m.last_name boss, m.salary sal_boss FROM employee e,employee m WHERE e.manager_id=m.employee_id AND e.salary > m.salary;
29
При использовании традиционного синтаксиса объединения мы указывали в инструкции FROM несколько таблиц, разделяя их запятыми. В новом синтаксисе (соответствующему стандарту ANSI SQL, определенному для SQL/92), начиная с Oracle9i можно указывать в инструкции FROM тип объединения вместе с ключевым словом JOIN. Например, чтобы выполнить внутреннее объединение таблиц department и location, можно написать: FROM employee у INNER JOIN department d Q2_21. Пример использования синтаксиса объединения, использующего JOIN DEMO@ORCL>SELECT l.location_id, d.name, l.regional_group FROM department d INNER JOIN location l ON d.location_id = l.location_id;
Другой вариант написания этого же запроса: DEMO@ORCL>SELECT location_id, d.name, l.regional_group FROM department d INNER JOIN location l USING (location_id); Следует обратить внимание на то, что к имени location_id извлекаемого общего связывающего таблицы столбца мы уже не добавляем квалификатор («d.» или «l.»). Преимущество нового синтаксиса объединения в том, что нет возможности непредумышленно сгенерировать декартово произведение (на контрольных работах студенты нередко допускают эту грубую ошибку). Если указано слово JOIN, необходимо задать условие объединения. Если декартово произведение действительно необходимо, оно задается явно (CROSS JOIN). Q2_22. Декартово произведение с использованием CROSS JOIN DEMO@ORCL> SELECT * FROM salary_grade a CROSS JOIN salary_grade b;
30
II.2.6. Внешние объединения Использование внешнего объединения ( или - соединения) для таблицы employee. Внешнее соединение задается опреатором (+) (символ плюс, заключенный в круглые скобки) и позволяет выбрать строки одной таблицы, для которых в другой таблице нет строк, соответствующих условию соединения. Оператор (+) помещается на той стороне соединяющего условия, которая соответствует таблице с отсутствующими данными. Он предписывает Oracle в случае отсутствия строк, удовлетворяющих условию соединения, возвращать NULL (неопределенное значение) для всех выражений списка выборки, которые содержат имена столбцов таблицы с отсутствующими данными. Q2_23. Получить перечень мест расположения отделов с указанием отделов, расположенных в них DEMO@ORCL>SELECT l.regional_group, d.name FROM department d, location l WHERE d.location_id(+)=l.location_id ORDER BY l.regional_group;
Regional_group «KIEV» и «MOSCOW» представлены, а отделов в таблице department для этих regional_group нет. Q2_24. В синтаксисе стандарта ANSI SQL, определенного для SQL/92 (начиная с Oracle9i) можно указывать в инструкции FROM тип объединения вместе с ключевыми словами RIGHT JOIN. Выполним предыдущий запрос использованием RIGHT JOIN: DEMO@ORCL> SELECT l.regional_group, d.name FROM department d RIGHT JOIN location l USING (location_id) ORDER BY l.regional_group; Результат тот же, что и в предыдущем запросе. Поменяем местами таблицы department, location в последнем запросе: DEMO@ORCL> SELECT l.regional_group, d.name FROM location l RIGHT JOIN department d USING (location_id) ORDER BY l.regional_group;
31
Внешнее объединение не «сработало». Справа надо вставлять название той таблицы, в которой есть информация regional_group «MOSCOW», «KIEV»). Если мы заменим в последнем запросе «RIGHT JOIN» на «LEFT JOIN», внешнее объединение отработает верно: DEMO@ORCL> SELECT l.regional_group, d.name FROM location l LEFT JOIN department d USING (location_id) ORDER BY l.regional_group;
Отметим, что в нашей учебной базе данных не только есть regional_group, в которых нет отделов, но и есть отделы, не принадлежащие ни к какой regional_group. Запрос, который бы показал и отделы, не принадлежащие regional_group, и regional_group, не имеющие отделов, должен использовать «FULL OUTER JOIN». DEMO@ORCL>SELECT l.regional_group, d.name, d.department_id FROM location l FULL OUTER JOIN department d USING (location_id) ORDER BY l.regional_group;
Q2_25. Комплексный пример использования объединений (внутренних и внешних).
32
Для отделов с номерами больше 20 извлечь информацию: фамилия служащего, его должность, номер тдела, в котором служащий работает, название отдела, место расположения отдела DEMO@ORCL> SELECT e.last_name AS name, j.function, d.department_id , d.name AS department_name, l.regional_group FROM employee e, department d, location l, job j WHERE e.job_id = j.job_id(+) AND e.department_id (+) = d.department_id AND d.location_id = l.location_id AND d.department_id>20 ORDER BY d.department_id;
II.2.7. Иерархические запросы Q2_26. В таблице EMPLOYEE можно проследить уровни подчиненности сотрудников фирмы /1/ DEMO@ORCL> SELECT level, department_id, employee_id, manager_id,last_name, job_id, salary FROM employee CONNECT BY PRIOR employee_id=manager_id START WITH manager_id IS NULL;
33
Для президента фирмы (KING) руководитель отсутствует (в столбце manager_id значение NULL). В запросе есть часть «START WITH manager_id IS NULL», чтобы «раскрутить» всю иерархию подчиненности, начиная с корневой строки иерархии (с президента фирмы). Если мы не укажем в запросе эту часть, запрос выведет главную иерархию, начинающуюся с KING-а, а также все возможные частные иерархии. В столбце «level» выводится уровень иерархии в «дереве» подчиненности. Для служащих с level 2 непосредственным начальником является KING. Служащие с level 3 в запросе выводятся сразу вслед за строкой своего начальника с level 2 и т.д. Часть запроса «CONNECT BY PRIOR employee_id=manager_id» определяет, что значения столбца «employee_id» являются родительскими, предшествующими (PRIOR) к дочернему столбцу «manager_id». В выводимой информации видно, что значения столбца «employee_id» располагаются выше тех же значений в столбце«manager_id». Сначала выводится, например, строка с last_name «KING», а затем только все строки со служащими, начальником которых является KING. Это и соответствует отношению при выводе информации по запросу «родитель (employee_id)» -«дочерний (manager_id)». Если мы выполним: DEMO@ORCL> SELECT level, department_id, employee_id, manager_id,last_name, job_id , salary FROM employee CONNECT BY PRIOR manager_id = employee_id START WITH manager_id =7566;
то увидим, что значения столбца «manager_id» располагаются выше (PRIOR) тех же значений столбца «employee_id» . Иерархия «разворачивается» от подчиненного к начальнику. А так как у служащего с номером 7566 (JONES) два подчиненных, эта иерархия выводится дважды: сначала для подчиненного JONES-а с last_name «SCOTT», затем для подчиненного JONES-а с last_name «FORD». Это соответствует при выводе информации по запросу отношению «родитель (manager_id)» - «дочерний (employee_id)». Q2_27. В нижеприводимом запросе /1/ используется удобное для восприятия форматирование вывода с помощью функции LPAD и псевдостолбца LEVEL Предварительно отформатируем используемый при выводе столбец, соответствующий алиасу «tree»: DEMO@ORCL>COL tree FORMAT A20 DEMO@ORCL> SELECT LPAD(' ', (LEVEL-1)*3)||LEVEL||' '||e.last_name AS tree, e.employee_id, e.manager_id, j.function FROM employee e, job j WHERE e.job_id = j.job_id CONNECT BY PRIOR e.employee_id=e.manager_id START WITH e.last_name='KING';
34
Q2_28. Из дерева иерархии сотрудников можно выделить отдельные ветви. Выделим строки, связанные с отделом 20 DEMO@ORCL> SELECT LPAD(' ',(LEVEL-1)*3)||LEVEL||' '||last_name AS tree, employee_id, manager_id , job_id FROM employee CONNECT BY PRIOR employee_id = manager_id AND department_id = 20 START WITH manager_id IS NULL;
Q2_29 Показать все ветви части общего дерева иерархии, выделенной предыдущим запросом, с указанием длины пути от родителя к потомку в этой ветви и самого пути от начальника к подчиненному для этой ветви /4/ DEMO@ORCL>SELECT last_name "Employee", CONNECT_BY_ROOT last_name "Manager", LEVEL-1 "Pathlen", SYS_CONNECT_BY_PATH(last_name, '/') "Path" FROM employee WHERE LEVEL > 1 and department_id = 20 CONNECT BY PRIOR employee_id = manager_id;
35
Выведены все возможные ветви для сотрудников отдела 20: SCOTT →ADAMS (одна срока), FORD → SMITH (одна строка), JONES и четыре его подчиненных (четыре строки), KING и пять его подчиненных из 20-го отдела (пять строк). Функция SYS_CONNECT_BY_PATH(last_name, '/') в иерархических запросах формирует путь от начальника к подчиненному, а часть запроса «CONNECT_BY_ROOT last_name "Manager"» позволяет вывести из нашей части (для отдела 20) общего дерева иерархии фирмы всех возможных начальников. Q2_30. Иерархический запрос позволяет агрегировать данные по ветвям дерева. Вычислим нарастающим итогом по всем ветвям дерева от подчиненных к начальнику зарплату служащих отдела 20, начиная с подчиненных/4/: DEMO@ORCL>SELECT name, SUM(salary) "Total_Salary" FROM (SELECT CONNECT_BY_ROOT last_name as name, Salary FROM employee WHERE department_id = 20 CONNECT BY PRIOR employee_id = manager_id) GROUP BY name;
II.3. Функции Oracle SQL Общее количество SQL функций в Otacle10g – более двухсот (см. раздел документации Oracle10g D:\Ora10.2_.doc\server.102\b14200\functions001.htm). По типу функции разделяются на числовые, символьные, функции для работы с датами, функции общего сравнения, функции преобразования, агрегатные функции, аналитические функции, функции для работы с большими объектами и др. Рассмотрим более подробно некоторые типы функций. II.3.1. Некоторые символьные функции Q3_1. Выведем на нижнем регистре названия отделов DEMO@ORCL>SELECT LOWER(name) FROM department;
36
Вывод на верхнем регистре реализуется функцией UPPER(название столбца или текст) Q3_2. Выведем названия отделов так, чтобы первая буква названия выводилась на верхнем регистре, используя функцию INITCAP() DEMO@ORCL>SELECT INITCAP(LOWER(name)) FROM department;
Q3_3. Дополним выводимые названия отделов слева символом «_» так, чтобы при этом выводимое название вместе с добавленными символами имело 20 символов DEMO@ORCL>SELECT LPAD(name, 20 ,’_’) FROM department;
Q3_4. Дополним выводимые названия отделов справа символом «*», так, чтобы при этом выводимое название вместе с добавленными символами имело 20 символов DEMO@ORCL>SELECT RPAD(name, 20 ,'*') FROM department;
37
Q3_5. Выведем наряду с описанием продукта усеченное слева описание продукта (удалением из описания продукта начального слова «YELLOW ») только для продуктов, описание которых начинается словом «YELLOW» demo@10g>SELECT description, LTRIM(description, 'YELLOW') AS "Shorted Description" FROM product WHERE description LIKE 'YELLOW%';
Q3_6. Выведем наряду с названием отдела усеченное справа название отдела (удалением из названия отдела завершающей части слова «ATIONS ») только для отделов, описание которых начинается буквой «O» demo@10g>SELECT name, RTRIM(name, 'ATIONS') AS "TOPPED NAME" FROM department WHERE name LIKE 'O%';
Q3_7. Выведем среднюю часть описания продукта, начинающегося на букву «W» demo@10g>SELECT description, SUBSTR(description, 6,8) AS "Middle" FROM product WHERE description LIKE 'W%';
Функция substr «вырезает» из описания продукта 8 букв, начиная с шестой. Q3_8. Определим, сколько раз символ «S» встречается в названиях отделов /1/. Сделаем это за несколько шагов.
38
DEMO@ORCL>SELECT name FROM department WHERE department_id IN(10, 20, 30, 40); NAME -------------ACCOUNTING RESEARCH SALES OPERATIONS
DEMO@ORCL>SELECT length(name) FROM department WHERE department_id IN(10,20,30,40); LENGTH(NAME) -----------10 8 5 10
DEMO@ORCL>SELECT translate(name,'AS','A') FROM department WHERE department_id IN(10,20,30,40); TRANSLATE(NAME -------------ACCOUNTING REEARCH ALE OPERATION
Функция translate в названии отдела, содержащемся в столбце name, заменяет символы из этого названия, если они встречаются в первом списке ('AS') на символы из второго списка ('A'). Причем для символа на первой позиции в первом списке для замены берется символ на первой позиции из второго списка, для символа на второй позиции из первого списка должен браться символ на второй позиции из второго списка и т.д. При такой замене 'A' "меняется" на 'A', 'S' меняется на ..., а вот для 'S' нет замены, так как список замен включает только одну букву 'A' и на второй позиции списка замен нет букв. В этом случае (когда нет замены) Oracle убирает букву 'S' из названия отдела, содержащегося в столбце name. DEMO@ORCL>SELECT LENGTH(TRANSLATE(name,'AS','A')) AS "Длина без символа S" FROM department WHERE department_id in(10, 20, 30, 40); Длина без символа S ------------------10 7 3 9
11 строк выбрано. DEMO@ORCL>SELECT LENGTH(name) - LENGTH(TRANSLATE(name,'AS','A')) AS "Число букв S" FROM department WHERE department_id in(10, 20, 30, 40);
39 Число букв S -----------0 1 2 1
II.3.2. Некоторые функции даты и времени Таблица II.2. Наиболее распространенные функции обработки календарных дат Функция
Возвращаемое значение
Пример использования
Результат
sysdate
Текущие дата и время
SELECT sysdate FROM dual;
28-FEB-99 (при вызове 28 февраля 1999 года)
last_day
Последний день месяца
SELECT last_day(sysdate) FROM dual;
31-MAR-99 (при вызове 12 марта 1999 года)
add_months(d,n)
Добавляет к дате d (или вычитает из нее) n месяцев
SELECT add_months(sysdate,2) FROM dual;
18-MAY-99 (при вызове 18 марта 1999 года)
months_between(f, s)
Число месяцев между датой f и датой s
13 (при вызове в апреле 1998 года)
next_day(d,day)
Ближайший указанный день недели day после даты d
SELECT months_between(sysdate, '12-MAR-99') FROM dual; SELECT next_day(sysdate,'Monday') FROM dual;
05-JAN-98 (при вызове 30 декабря 1997 года)
Для работы с функциями обработки календарных дат (табл. II.2) надо знать собственно форматы представления дат. Поэтому далее приведем форматы представления дат в Oracle (табл. II.3). Таблица II.3. Наиболее распространенные форматы представления дат Функция
Возвращаемое значение
Пример использования
Результат
Y, YY или YYY
Последние одна, две или три цифры календарного года
SELECT to_char(sysdate,'YYY') FROM dual;
999 (для всех дней 1999 года)
SYEAR или YEAR
Полный номер календарного года; при использовании SYEAR перед датами до н.э. ставится минус
SELECT to_char(sysdate, 'SYEAR') FROM dual;
TWO THOUSAND EIGHT (для вызова в 2008 г.)
Q
Квартал года (первый квартал продолжается с января по март)
SELECT to_char(sysdate,'Q') FROM dual;
2 (для всех дней июня)
MM
Номер месяца (в формате 01-12, где 12 соответствует декабрю)
SELECT to_char(sysdate,'MM') FROM dual;
12 (для всех дней декабря)
RM
Номер месяца римскими цифрами
SELECT to_char(sysdate,'RM') FROM dual;
IV (для всех дней апреля)
Month
Месяц в виде строки из девяти символов
SELECT to_char(sysdate, 'Month') FROM dual;
May и шесть пробелов (для всех дней мая)
40 WW
Номер недели в году
SELECT to_char(sysdate,'WW') FROM dual;
24 (при вызове 13 июня 1998 года)
W
Номер недели в месяце
SELECT to_char(sysdate,'W') FROM dual;
1 (при вызове 1 октября 1995 года)
DDD
SELECT to_char(sysdate,'DDD') FROM dual;
363 (при вызове 29 декабря 1999 года)
DD
Порядковый номер дня в году: 1 января — день номер 001, 1 февраля — 032 и т.д. День месяца
SELECT to_char(sysdate,'DD') FROM dual;
04 (при вызове 4 октября)
D
День недели (в формате 1-7)
SELECT to_char(sysdate,'D') FROM dual;
1 (при вызове 14 марта 1999 года)
DY
Сокращенное название дня недели
SELECT to_char(sysdate,'DY') FROM dual;
SUN (при вызове 28 марта 1999 года)
НН или НН12
Час по 12-часовой системе
SELECT to_char(sysdate,'HH') FROM dual;
02 (в момент времени 02:08, т.е. в 2 часа 8 минут после полуночи)
НН24
Час по 24-часовой системе
SELECT to_char(sysdate,'HH24') FROM dual;
14 (в 2 часа 8 минут пополудни, т.е. в 14:08)
Ml
Минуты (0-59)
SELECT to_char(sysdate,'MI') FROM dual;
17 (в 16:17, т.е. в 4 часа 17 минут пополудни)
SS
Секунды (0-59)
SELECT to_char(sysdate,'SS') FROM dual;
22 (в момент времени 11:03:22)
Следует привести команды, полезные при заполнении таблиц информацией из SQL скрипта, применять которые нужно, если настройки даты на своем компьютере отличаются от формата даты в скриптах. Параметры настройки даты на своем компьютере определяют командой DEMO@ORCL>SELECT * FROM nls_session_parameters WHERE parameter IN ('NLS_LANGUAGE', 'NLS_DATE_FORMAT'); PARAMETER -----------------------------NLS_LANGUAGE NLS_DATE_FORMAT
VALUE --------------------------------RUSSIAN DD.MM.RR
Такие настройки даты на компьютере автора. В скриптах же заполнения таблиц схемы «DEMO» формат даты «DD-MON-YYYY», причем месяц пишется на английском («JUN», «FEB» и т.д.). Поэтому автору для успешного заполнения таблиц схемы демо необходимо выполнить две команды: ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-YYYY'; ALTER SESSION SET NLS_LANGUAGE='AMERICAN'; Эти две команды присутствуют в начале скрипта создания предметной области DEMO. Q3_9. Определим в днях разницу дат заказа покупки (order_date) покупателем и ее оплаты (ship_date) DEMO@ORCL>SELECT (ship_date - order_date) AS "Задержка оплаты"
41
FROM sales_order WHERE TO_CHAR(order_date, 'YY')=89; Задержка оплаты --------------6 1 3 12 16 15 13 4 16 1 11 3 8 13 7 3 16 5 15 13 3 21 строк выбрано.
Q3_10. Определим заказы, перешедшие на другой год DEMO@ORCL>SELECT order_id, customer_id FROM sales_order WHERE TO_CHAR(ship_date,'yyyy') - TO_CHAR(order_date,'YYYY')=1; ORDER_ID CUSTOMER_ID -------------------549 226 559 222
II.3.3. Некоторые функции преобразования В Oracle для таблиц предметной области существует псевдостолбец с именем ROWID, значения которого определяют место хранения строки таблицы с этим значением ROWID (номер файла, номер блока в этом файле, номер записи в этом блоке). Q3_11. Работа с ROWID, ROWNUM Извлечение столбца ROWID: DEMO@ORCL>SELECT rowid, last_name FROM employee WHERE department_id=10;
Попробуем вывести только одну строку с номером (rownum – номер выводимой запросом строки) «1» DEMO@ORCL>SELECT rowid, last_name FROM employee WHERE rownum=1; ROWID LAST_NAME -------------------------------AAANVAAAEAAAAXnAAA KING
42
Для rownum=2: DEMO@ORCL>SELECT rowid, last_name FROM employee WHERE rownum=2; строки не выбраны Так для любого rownum, не равного единице. Это ограничение можно обойти так: DEMO@ORCL>SELECT rn, ri, last_name FROM (SELECT rownum rn, rowid ri, last_name FROM employee) WHERE rn=2; RN ---------2
RI -----------------AAANVAAAEAAAAXnAAB
LAST_NAME --------------DOYLE
Преобразуем rowid в вид restricted, в котором для каждой строки явно указывается номер файла, номер блока в файле ОС и номер строки в этом блоке DEMO@ORCL>SELECT rn, rnr, last_name, object_id FROM (SELECT rownum rn, last_name, DBMS_ROWID.ROWID_OBJECT(rowid) AS object_id, DBMS_ROWID.ROWID_TO_RESTRICTED(rowid,0) rnr FROM employee) WHERE rn<5;
В представленном выводе object_id – номер таблицы employee, в столбце rnr число «0004» идентификатор файла, число «000005E7» номер блока, оставшаяся часть rnr – номер строки в блоке. Убедимся в этом, выполнив запрос к таблице user_objects словаря Oracle: DEMO@ORCL>SELECT object_name FROM user_objects WHERE object_id=54592; OBJECT_NAME -------------------EMPLOYEE
Для получения номера файла воспользуемся функцией ROWID_TO_ABSOLUTE_FNO в составе программного модуля DBMS_ROWID: DEMO@ORCL>SELECT DBMS_ROWID.ROWID_TO_ABSOLUTE_FNO(rowid,'demo','employee') FROM employee WHERE rownum=1; DBMS_ROWID.ROWID_TO_ABSOLUTE_FNO(rowid,'demo','employee') ------------------------------------------------------4
Если мы пользователем SYS/SYS AS SYSDBA выполним команду: sys@10g> SELECT file_name FROM dba_data_files WHERE file_id=4; то получим: FILE_NAME -----------------------------------------------------------D:\ORACLE\PRODUCT\10.2.0\ORADATA\ORCL\USERS01.DBF
43
Извлечем с помощью функции ROWID_BLOCK_NUMBER в составе программного модуля DBMS_ROWID номер блока, в котором хранятся строки таблицы employee: demo@10g>SELECT DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) AS block_number FROM employee; BLOCK_NUMBER -----------1511 5E716(см. запрос со столбцом rnr выше) = 5*642 + 14*161 + 7*160 = 1280 + 224 + 7 = 1511. Если мы пользователем SYS/SYS AS SYSDBA (у пользователя DEMO нет прав на работу с таблицей col$) выполним: SYS@ORCL> SELECT ROUND(SUM(in_block_count_string)/count(*), 0) AS srednee_chislo_strok_v_bloke FROM (SELECT COUNT(DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)) AS in_block_count_string FROM col$ GROUP BY DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid)); SREDNEE_CHISLO_STROK_V_BLOKE ---------------------------74 то получим среднее число записей таблицы col$ в одном блоке. Таблица col$ взята здесь для примера таблицы с относительно большим количеством строк; для хранения в ней ~60 тысяч строк требуется ~700 блоков. Выполнять такой подсчет для таблиц пользователя DEMO нет смысла из-за малого количества строк в таблицах этого пользователя. Все таблицы этого пользователя, кроме item, хранят строки в одном блоке (item – в трех). Q3_12. Функция NUMTODSINTERVAL преобразования числа в интервал (дней, часов, минут, секунд). Перед примером ее использования отформатируем столбцы: DEMO@ORCL>COL число_дней_в_дни FORMAT A20 DEMO@ORCL>COL число_часов_в_дни FORMAT A20 DEMO@ORCL>COL число_минут_в_дни FORMAT A20 DEMO@ORCL>COL число_секунд_в_дни FORMAT A20 DEMO@ORCL>SELECT employee_id, (NUMTODSINTERVAL(employee_id,'DAY')) AS "ЧИСЛО_дней_в_дни", (NUMTODSINTERVAL(employee_id,'HOUR')) AS "число_часов_в_дни", (NUMTODSINTERVAL(employee_id,'MINUTE')) AS "Число_минут_в_дни", (NUMTODSINTERVAL(employee_id,'SECOND')) AS "Число_секунд_в_дни" FROM employee WHERE department_id=10;
44
Q3_13. Эту функцию можно использовать и для преобразования числа, получаемого из даты в интервал (дней, часов, минут, секунд) с начала месяца DEMO@ORCL>SELECT employee_id, hire_date, NUMTODSINTERVAL(TO_CHAR(hire_date, 'DD'), 'DAY') FROM employee WHERE department_id=10;
Q3_14. Можно вычитать и складывать значения, возвращаемые функцией NUMTODSINTERVAL. DEMO@ORCL>SELECT NUMTODSINTERVAL(TO_CHAR(ship_date, 'DD'),'day') NUMTODSINTERVAL(TO_CHAR(order_date, 'dd'),'DAY') day_diff FROM sales_order WHERE TO_CHAR(order_date, 'YY')=89;
45
Q3_15. Функция NUMTOYMINTERVAL преобразует число(лет, месяцев) в интервал DEMO@ORCL>SELECT employee_id, NUMTOYMINTERVAL(employee_id,'YEAR') FROM employee WHERE department_id=10;
DEMO@ORCL>SELECT employee_id, NUMTOYMINTERVAL(employee_id,'MONTH') FROM employee WHERE department_id=10;
Q3_16. Функция SCN_TO_TIMESTAMP(ORA_ROWSCN) преобразует псевдостолбец ORA_ROWSCN (содержащий номер последней транзакции с таблицей) во временную метку – время выполнения транзакции DEMO@ORCL>UPDATE employee SET salary=3000 WHERE employee_id=7369; DEMO@ORCL>commit; DEMO@ORCL>SELECT SCN_TO_TIMESTAMP(ORA_ROWSCN) FROM employee WHERE employee_id = 7369; SCN_TO_TIMESTAMP(ORA_ROWSCN); ---------------------------------05.08.08 19:56:02,000000000
Через несколько минут: DEMO@ORCL>UPDATE employee SET salary=800 WHERE employee_id=7369; DEMO@ORCL>commit; DEMO@ORCL>SELECT SCN_TO_TIMESTAMP(ORA_ROWSCN) FROM employee WHERE employee_id = 7369; SCN_TO_TIMESTAMP(ORA_ROWSCN) ---------------------------------05.08.08 19:59:17,000000000
Q3_17. Использование функции TO_CHAR(date). Вывести дату и точное время оформления заказов на покупки, сделанные в ноябре 1989 г. DEMO@ORCL>SELECT TO_CHAR(ship_date,'DD-MON-YYYY HH24:MI:SS') FROM sales_order WHERE TO_CHAR(ship_date,'YYYY')=1989 AND TO_CHAR(ship_date,'MM')=11;
46
Так как в параметрах функции вывод месяцев показан, как «MON», месяц выводится символами «НОЯ» вне зависимости от формата даты в сеансе: DEMO@ORCL>SELECT SYSDATE FROM dual; SYSDATE -------06.08.08
Q3_18. Использование функции to_date(строка символов). Изменить дату оформления некоторых заказов. DEMO@ORCL>UPDATE sales_order SET ship_date=TO_DATE('30.11.89') WHERE TO_CHAR(ship_date,'yyyy')=1989 AND TO_CHAR(ship_date,'MM')=11; 5 строк обновлено. Измененение даты представлено символьной строкой '30.11.89', формат которой совпадает с форматом сеанса. Если такого совпадения нет, в функции to_date надо указывать дополнительные параметры. DEMO@ORCL>UPDATE sales_order SET ship_date=TO_DATE('30-nov-89','DD-MON-YY' , 'NLS_DATE_LANGUAGE=AMERICAN') WHERE TO_CHAR(ship_date,'YYYY')=1989 AND TO_CHAR(ship_date,'MM')=11; 5 строк обновлено. !!! Теперь обязательно выполним команду отмены транзакции demo@10g>ROLLBACK; Откат завершен II.3.4. Функции DECODE, CASE, DUMP, ORA_HASH, VSIZE Q3_19. Формирование отчета, в котором содержимое столбца разворачивается в ряд столбцов отчета. Отчет должен содержать по строкам список сотрудников (номера и имена служащих), по столбцам наименования отделов, а на пересечении знак ‗+‘, если служащий работает в соответствующем отделе (только для отделов 10, 20, 30) DEMO@ORCL>SELECT employee_id, last_name, (SELECT DECODE(department_id,10,' + ',NULL) FROM department WHERE employee.department_id=department.department_id) acc_new_york, (SELECT DECODE(department_id,20,' + ',NULL) FROM department WHERE employee.department_id=department.department_id) res_dallas, (SELECT DECODE(department_id,30,' + ',NULL) FROM department WHERE employee.department_id=department.department_id) sal_chicago FROM employee WHERE department_id in(10, 20, 30);
47
В этом запросе создается пять столбцов, три последних из которых создаются с помощью функции decode. Функция decode в данном запросе обрабатывает значения столбца department_id (первым параметром функции decode является название столбца, для которого эта функция используется). Если в этом столбце извлекаемой строки для части запроса с алиас «acc_new_york» содержится значение «10» (второй параметр функции decode в данной части запроса имеет значение «10»), функция decode вернет символ «+» (третий параметр функции decode имеет значение «+»), в противном случае – NULL (последний параметр функции decode имеет значение «NULL»). Если в этом столбце извлекаемой строки содержится значение «20» (для извлекаемой части запроса с алиас «res_dallas»), функция decode вернет символ «+», в противном случае – NULL. Если, наконец, в этом столбце извлекаемой строки содержится значение «30» (для извлекаемой части запроса с алиас «sal_chicago»), функция decode вернет символ «+», в противном случае – NULL. Q3_20. А теперь усложним вывод отчета. На пересечении строк (сотрудников) и столбцов (названий отдела) надо выставить зарплату сотрудника в этом отделе (только для отделов 10, 20, 30) DEMO@ORCL>SELECT employee_id, last_name, (SELECT decode(department_id,10,salary,NULL) FROM department WHERE employee.department_id=department.department_id) acc_new_york, (SELECT decode(department_id,20,salary,NULL) FROM department WHERE employee.department_id=department.department_id) res_dallas, (SELECT decode(department_id,30,salary,NULL) FROM department WHERE employee.department_id=department.department_id) sal_chicago FROM employee WHERE department_id in(10,20,30);
48
Q3_21. Повторим запросы Q3_19 и Q3_20 в другом варианте с использованием CASE выражения: DEMO@ORCL>SELECT employee_id, last_name, (CASE WHEN department_id=10 THEN ' + ' ELSE NULL END) acc_new_york, (CASE WHEN department_id=20 THEN ' + ' ELSE NULL END) res_dallas, (CASE WHEN department_id=30 THEN ' + ' ELSE NULL END) sal_chicago FROM employee WHERE department_id IN(10, 20, 30); DEMO@ORCL>SELECT employee_id, last_name, (CASE WHEN department_id=10 THEN salary ELSE NULL END) acc_new_york, (CASE WHEN department_id=20 THEN salary ELSE NULL END) res_dallas, (CASE WHEN department_id=30 THEN salary ELSE NULL END) sal_chicago FROM employee WHERE department_id IN(10, 20, 30); Q3_22. С помощью функции DUMP определим код типа данных, длину записанной в ячейке столбца информации, набор символов (CharacterSet), коды последовательности символов (указанной длины), начиная с указанной стартовой позиции. DEMO@ORCL>SELECT DUMP(last_name, 8) FROM employee WHERE department_id=10;
Если вместо «8» поставим «10» или «16» получим коды символов в соответствующей системе (десятичной или шестнадцатиричной). DEMO@ORCL>SELECT DUMP(last_name, 10, 2, 2) FROM employee WHERE department_id=10;
DEMO@ORCL>SELECT DUMP(last_name,1016) FROM employee WHERE department_id=10;
Добавление 1000 к цифре кодировки (16 в последнем запросе) позволяет вывести набор символов (CharacterSet). DEMO@ORCL>COL DUMP(salary,1016) format a25 DEMO@ORCL>SELECT salary,DUMP(salary,1016) FROM employee WHERE department_id=10;
49
Q3_23. Функция VSIZE выводит число байт во внутреннем представлении Oracle DEMO@ORCL>SELECT last_name, VSIZE (last_name) "BYTES" FROM employee WHERE department_id = 10;
Q3_24. Функция ORA_HASH вычисляет хэш значение для значений столбца. В нижеприводимом варианте хэш значение разыгрывается в диапазоне чисел 0 – 4294967295. DEMO@ORCL>SELECT ORA_HASH(salary) FROM employee WHERE department_id=10; ORA_HASH(SALARY) ---------------3983772665 929675335 4269363183
Можно изменить диапазон (установим, например, 100) и задать начальное значение последовательности случайных чисел (установим, например, 5), которая используется при формировании хэш значения DEMO@ORCL>SELECT ORA_HASH(salary,100,5) FROM employee WHERE department_id=10; ORA_HASH(SALARY,100,5) ---------------------97 79 9
II.3.5. Агрегатные и аналитические функции Выше мы уже ознакомились с некоторыми агрегатными функциями (max(), min(), avg()). Общее число агрегатных и аналитических функций в Oracle10g более 50. Аналитические функции позволяют производить ранжирование (ranking), перенос сводных результатов (moving aggregates), сравнение данных за различные периоды (period comparisons), соотношение итогов (ratio of total), получение совокупных сводных результатов (cumulative aggregates) и другие действия. Q3_25. Выполним запрос, устанавливающий ранг сотрудников компании, обслуживающих покупателей (salesperson_id в таблице customer) по суммам продаж «их» покупателям – за все годы продаж. Максимальной сумме продаж будет соответствовать ранг «1», следующей за максимальной – ранг «2» и т.д. Если сумма продаж одинаковая, то и ранг должен быть одинаковым.
50
DEMO@ORCL>SELECT c.salesperson_id, NVL(ROUND(SUM(so.total),0),0) tot_sales, RANK() OVER (ORDER BY NVL(SUM(so.total),0) DESC) sales_rank FROM customer c, sales_order so WHERE c.customer_id=so.customer_id(+) GROUP BY salesperson_id;
Если бы в компании был сотрудник, принесший ей убыток (tot_sales<0), ранг этого работника был бы установлен «12», так как у двух сотрудников перед тем был установлен одинаковый ранг «10». При использовании функции DENSE_RANK: DEMO@ORCL>SELECT c.salesperson_id, NVL(ROUND(SUM(so.total), 0), 0) tot_sales, DENSE_RANK() OVER (ORDER BY NVL(SUM(so.total), 0) DESC) sales_rank FROM customer c, sales_order so WHERE c.customer_id=so.customer_id(+) group by salesperson_id; Результат получаем такой же, как и предыдущий. Но если бы в компании был сотрудник, принесший ей убыток, ранг этого работника был бы установлен «11». В этом только отличие DENSE_RANK() от RANK(). Q3_26. Усложним запрос Q3_25. Установим ранг сотрудников внутри отделов, в которых они работают (опять по сумме продаж за все годы): demo@10g>SELECT e.department_id, c.salesperson_id, NVL(ROUND(SUM(so.total), 0), 0) AS tot_sales, RANK() OVER (PARTITION BY department_id ORDER BY NVL(SUM(so.total), 0) DESC) sales_rank FROM employee e,customer c, sales_order so WHERE c.salesperson_id=e.employee_id AND c.customer_id=so.customer_id(+) GROUP BY (department_id,salesperson_id);
51
В запросе использована совместно с RANK() опция PARTITION BY, в которой указывается, в привязке к какому столбцу идет детализация ранга (в нашем случае к столбцу «идентификатор отдела»). Q3_27. Выполним запрос: определить, подразделив отчет по группам сотрудников, подчиняющихся своим менеджерам (PARTITION BY manager_id), нарастающим итогом количество сотрудников, принятых на работу в заданном временном интервале (100 дней), отсчитываемом от даты принятия на работу под управлением «своего» менеджера первого сотрудника. Для задания интервала в запросе используется опция «RANGE NUMTODSINTERVAL(100, 'DAY') PRECEDING». Здесь «RANGE» диапазон, NUMTODSINTERVAL(100, 'DAY') – интервал в 100 дней, «preceding» означает разницу даты приема на работу (HIRE_DATE – в нашем запросе) очередного извлекаемого запросом сотрудника с датой первого принятого в этом временном интервале на работу сотрудника (в группе сотрудников под управлением «своего» менеджера – ведь мы используем «PARTITION BY manager_id»). Если в группе сотрудников, подчиненных одному менеджеру, дата приема на работу очередного извлекаемого запросом сотрудника отличается от даты первого принятого на работу под управлением этого менеджера сотрудника более, чем на сто дней, отсчет количества принятых на работу сотрудников вновь начинается с единицы. DEMO@ORCL> SELECT manager_id, last_name, hire_date, COUNT(*) OVER (PARTITION BY manager_id ORDER BY hire_date RANGE NUMTODSINTERVAL(100, 'DAY') PRECEDING) AS t_count FROM employee WHERE manager_id is NOT NULL;
В отработанном запросе для менеджера с manager_id=7505 к 01 июня 1985 года на работу было принято 4 сотрудника (разница даты приема на работу (четвертым) ROSSа и даты приема на работу (первым) PETERSа меньше 100 дней). Следующим под управление этого менеджера был принят на работу JENSEN 15 января 1987г. Разница
52
этой даты с датой приема на работу PETERSа (31 марта 1985г.) больше 100 дней, поэтому счет количества принятых на работу сотрудников под управлением этого менеджера опять начинается с единицы (T_COUNT=1) . Если мы выполним тот же запрос с временным интервалом в 1000 дней, внутри групп отдельных менеджеров у нас уже не будет повторяющихся цифр T_COUNT для строк с одним и тем же менеджером. Q3_28. Продемонстрируем формирование агрегатных данных опцией ROLLUP. ROLLUP обеспечивает агрегирование на каждом уровне, заданном столбцами в GROUP BY. Это приводит к получению информации, которую нельзя было получить раньше без дополнительного кодирования. Следующий запрос извлекает номер отдела, код должности, сгруппированные по этим двум столбцам суммы зарплат, но также и промежуточные итоги общую сумму зарплаты по отделам. DEMO@ORCL>SELECT department_id, job_id, SUM(salary) AS "Total SAL" FROM employee GROUP BY ROLLUP (department_id,job_id);
Поменяем порядок столбцов в опции ROLLUP: DEMO@ORCL>SELECT job_id,department_id, SUM(salary) AS "Total SAL" FROM employee GROUP BY ROLLUP (job_id, department_id);
53
Отформатируем вывод: DEMO@ORCL>COL department FORMAT A20 DEMO@ORCL>BREAK ON department DEMO@ORCL>SELECT NVL(TO_CHAR(department_id), 'Whole Company') AS department , DECODE(job_id, NULL, 'All Employees') AS job, SUM(salary) AS "Total SAL" FROM employee GROUP BY ROLLUP (department_id, job_id);
Q3_29. Использование GROUP BY с опцией CUBE В дополнение к групповым подитогам и общим итогам, созданным ROLLUP, CUBE автоматически вычисляет все возможные комбинации возможных подитогов. Выведем столбцы: код должности, номер отдела, суммарная зарплата, а также идентификаторы группирования по этим столбцам. DEMO@ORCL>SELECT job_id, department_id, SUM(salary), GROUPING(department_id), GROUPING(job_id) FROM employee GROUP BY CUBE(job_id, department_id);
54
Наличие «1» в столбце grouping(department_id) означает, что группирование проводится по номерам отделов – сумма зарплаты подсчитывается и выводится для строк с одинаковым номером отдела, «1» в столбце grouping(job_id) – по коду работы. Если в обоих столбцах значения «0», подсчитывается сумма по зарплате для этого кода работы и номера отдела. Q3_30. Получим имя работника, департамент и заработную плату; также требуется получить общую сумму заработной платы по департаментам и процент заработной платы конкретного служащего в сумме департамента и общей сумме (например, работник Х в департаменте Y получает 10 % заработной платы от суммы заработной платы его департамента и 1 % от заработной платы компании) /4/. DEMO@ORCL>SELECT employee.department_id, employee.last_name, employee.salary, sum(employee4.salary) cum_salary , round(100*employee.salary/employee2.salary_by_dept,1) AS pct_dept , round(100*employee.salary/employee3.salary_overall,1) AS pct_overall FROM employee , (SELECT department_id, sum(salary) AS salary_by_dept FROM employee GROUP BY department_id ) employee2 , (SELECT sum(salary) AS salary_overall FROM employee ) employee3 , employee employee4 WHERE employee.department_id = employee2.department_id AND employee.department_id = employee4.department_id AND (employee.salary > employee4.salary OR (employee.salary = employee4.salary AND employee.last_name >= employee4.last_name)) GROUP BY employee.department_id, employee.last_name, employee.salary , round(100*employee.salary/employee2.salary_by_dept,1) , round(100*employee.salary/employee3.salary_overall,1)
55
ORDER BY department_id, salary;
Теперь отработаем этот запрос с помощью аналитических функций. Сначала отформатируем столбцы, чтобы номер отдела повторно не выводился и между группами строк разных отделов была пустая строка: DEMO@ORCL>COLUMN pct_dept FORMAT 99.9 DEMO@ORCL>COLUMN pct_overall FORMAT 99.9 DEMO@ORCL>BREAK ON department_id SKIP 1 а теперь сам запрос: DEMO@ORCL>SELECT department_id, last_name, salary , SUM(salary) OVER (PARTITION BY department_id ORDER BY salary,last_name) AS cum_sal, ROUND(100*RATIO_TO_REPORT(salary) OVER (PARTITION BY department_id), 1 ) pct_dept, ROUND(100*RATIO_TO_REPORT(salary) OVER () , 1 ) AS pct_overall FROM employee ORDER BY department_id, salary; Получим такой же точно результат, какой был в предыдущем запросе, который выполнялся без аналитических функций. RATIO_TO_REPORT(salary) – краткая и удобная функция получения процентных долей как внутри отделов OVER (PARTITION BY department_id), так и по всей фирме OVER (). II.4. Другие команды языка манипулирования данными DМL и обработка транзакций II.4.1. Команды DML insert, update, delete Q4_1. Включение в таблицу одной строки DEMO@ORCL>INSERT INTOdepartment (department_id, name, location_id) VALUES(77, 'Teaching', null); 1 строка создана.
56
Напомним здесь, что при создании таблицы DEPARTMENT столбец location_id был объявлен foreign key с допущением null значений. Если бы вставляли новую строку в таблицу EMPLOYEE, мы не смогли бы вставить ее с null значением в столбце внешнего ключа department_id, так как при создании таблицы EMPLOYEE столбцу department_id было задано ограничение not null. Вернем таблицу DEPARTMENT к прежнему состоянию: DEMO@ORCL>ROLLBACK; Откат завершен. Повторим предыдущую команду немного в другом варианте: DEMO@ORCL>INSERT INTO department VALUES(77, 'Teaching', null); 1 строка создана. Если вставляются все столбцы, указывать после названия таблицы столбцы необязательно. Повторим откат: DEMO@ORCL>ROLLBACK; Откат завершен. Выполним вставку только в отдельные столбцы: DEMO@ORCL>INSERT INTO department(department_id,name) VALUES(77,'Teaching'); 1 строка создана. При вставке не всех столбцов их названия надо приводить после названия таблицы. Повторим откат: DEMO@ORCL>ROLLBACK; Откат завершен. Рассмотрим варианты вставки даты. Приведем структуру таблицы SALES_ORDER: DEMO@ORCL>DESC sales_order Имя ------------------------ORDER_ID ORDER_DATE CUSTOMER_ID SHIP_DATE TOTAL
Пусто? Тип -------------------NOT NULL NUMBER(4) DATE NUMBER(6) DATE NUMBER(8,2)
Посмотрим формат и language даты в нашем сеансе (см. раздел II.3.2): DEMO@ORCL>SELECT * FROM nls_session_parameters WHERE parameter in ('NLS_LANGUAGE', 'NLS_DATE_FORMAT');
В соответствии с этим форматом и выполним вставку: DEMO@ORCL>INSERT INTO sales_order VALUES(900, '21.07.89',208, '23.07.89', 2345); 1 строка создана. Выполним откат: DEMO@ORCL>ROLLBACK; Откат завершен. Воспользуемся при вставке даты функцией to_date():
57
DEMO@ORCL>INSERT INTO sales_order VALUES(900, TO_DATE('21.07.89'), 208, TO_DATE('23.07.89'), 2345); 1 строка создана. DEMO@ORCL>ROLLBACK; Откат завершен. А теперь вставим дату в другом формате: DEMO@ORCL>INSERT INTO sales_order(order_id, order_date, customer_id, ship_date, total) VALUES(900, TO_DATE('21-jul-1989', 'DD-MON-YYYY' , 'NLS_DATE_LANGUAGE=AMERICAN') , 208, TO_DATE('23-jul-1989', 'DD-MON-YYYY' , 'NLS_DATE_LANGUAGE=AMERICAN'), 2345); 1 строка создана. Отметим, что вставленная строка извлекает дату в формате сеанса: DEMO@ORCL>ROLLBACK; Откат завершен. DEMO@ORCL>SELECT * FROM sales_order WHERE order_id=900; ORDER_ID ORDER_DA CUSTOMER_ID SHIP_DAT ---------------------------------900 21.07.89 208 23.07.89
TOTAL ---------2345
Q4_2. Создание последовательности DEMO@ORCL>CREATE SEQUENCE departmentseq START WITH 60; Последовательность создана. Q4_3. Используем последовательность для генерации значений столбца department_id: DEMO@ORCL>INSERT INTO department VALUES(departmentseq.NEXTVAL, 'training', 122); 1 строка создана. DEMO@ORCL>SELECT * FROM department WHERE name='training'; DEPARTMENT_ID ------------60
NAME -------------training
LOCATION_ID ----------122
DEMO@ORCL>ROLLBACK; Откат завершен. Q4_4. Для вставки копированием с использованием запроса создадим таблицу S_GRADE, которая не будет иметь столбец PRIMARY KEY: DEMO@ORCL>CREATE TABLE s_grade AS SELECT * FROM salary_grade; DEMO@ORCL>SELECT * FROM s_grade;
DEMO@ORCL>INSERT INTO s_grade (SELECT * FROM s_grade); demo@10g>SELECT * FROM s_grade;
58
При наличии столбца ―primary key‖ Oracle не допустил бы вставку повторяющихся по primary key строк. Теперь удалим созданную таблицу s_grade: DEMO@ORCL>DROP TABLE s_grade; Таблица удалена. Q4_5. Вставка одной строки с использованием запроса: DEMO@ORCL>INSERT INTO (SELECT * FROM department) VALUES(90, 'teaching', 122); 1 строка создана. DEMO@ORCL>ROLLBACK; Откат завершен. Q4_6. Иногда требуется обеспечить ввод определенных значений столбца, не допуская другие значения для ввода. Такой ввод возможен с использованием WITH CHECK OPTION DEMO@ORCL> INSERT INTO (SELECT customer_id,salesperson_id,zip_code FROM customer WHERE zip_code = 10009 WITH CHECK OPTION) VALUES(333 , 7557,75051); INSERT INTO (SELECT customer_id,salesperson_id,zip_code * ошибка в строке 1: ORA-01402: представление WITH CHECK OPTION не соответствует фразе WHERE Ошибка зарегистрирована, так как в опции WITH CHECK OPTION указано допустимое для ввода значение zip_code = 10009, а вводимое значение 75051. DEMO@ORCL> INSERT INTO (SELECT customer_id,salesperson_id,zip_code FROM customer WHERE zip_code = 10009 WITH CHECK OPTION) VALUES(333 , 7557,10009); 1 строка создана. DEMO@ORCL>ROLLBACK; Откат завершен. Q4_7 Изменение строк таблицы командой UPDATE Создадим еще раз таблицу s_grade DEMO@ORCL>CREATE TABLE s_grade AS SELECT * FROM salary_grade; Таблица создана. DEMO@ORCL>SELECT * FROM s_grade;
59
Изменим в таблице s_grade значения столбца lower_bound, установив в этом столбце значения из столбца upper_bound таблицы salary_grade DEMO@ORCL>UPDATE s_grade SET lower_bound=(SELECT upper_bound FROM salary_grade sg WHERE sg.grade_id=s_grade.grade_id); 5 строк обновлено. DEMO@ORCL>SELECT * FROM s_grade;
DEMO@ORCL>ROLLBACK; Откат завершен. Q4_8. Выполним простое изменение строки таблицы department DEMO@ORCL>UPDATE department SET name='TEACHING' WHERE department_id=12; 1 строка обновлена. DEMO@ORCL>ROLLBACK; Откат завершен. Q4_9. «Передать» покупателей от продавца PETERS к продавцу SHAW, установив им кредитный рейтинг в 80% от среднего кредитного рейтинга всех покупателей, обслуживаемых (до этой передачи) продавцом SHOW DEMO@ORCL>UPDATE customer SET salesperson_id = (SELECT employee_id FROM employee where last_name = 'SHAW') , credit_limit=(SELECT ROUND(0.8*AVG(credit_limit),0) FROM customer WHERE salesperson_id= (SELECT employee_id FROM employee WHERE last_name = 'SHAW')) WHERE salesperson_id= (SELECT employee_id FROM employee WHERE last_name='PETERS'); 2 строк обновлено. Q4_10. Удаление строк из таблицы командой DELETE. Удаление сведений обо всех отделах, расположенных в boston DEMO@ORCL>DELETE FROM department WHERE location_id in (SELECT location_id FROM location WHERE regional_group='BOSTON'); 2 строк удалено. DEMO@ORCL>ROLLBACK; Откат завершен.
60
Q4_11. Удаление из таблицы location сведений о городах (regional_group), в которых нет отделов DEMO@ORCL>DELETE FROM location X WHERE NOT EXISTS (SELECT 1 FROM department WHERE location_id=X.location_id); 2 строк удалено. DEMO@ORCL>ROLLBACK; Откат завершен. Q4_12. Удалим дубликаты записей из таблицы s_grade, предварительно создав их. DEMO@ORCL>INSERT INTO s_grade (select* FROM s_grade); DEMO@ORCL>SELECT rowid, s_grade.* FROM s_grade;
DEMO@ORCL>DELETE FROM s_grade X WHERE rowid > (SELECT MIN(rowid) FROM s_grade Y WHERE X.grade_id=Y.grade_id); 5 строк удалено. DEMO@ORCL>SELECT rowid, s_grade.* FROM s_grade;
II.4.2. Обработка транзакций Завершим предыдущую транзакцию DEMO@ORCL>commit; Q4_13. Начнем новую транзакцию. В ходе ее создадим точку сохранения и вернемся к ней, восстановив изменения после нее. DEMO@ORCL>SELECT * FROM s_grade;
61
DEMO@ORCL>DELETE FROM s_grade WHERE grade_id=5; 1 строка удалена. DEMO@ORCL>SELECT * FROM s_grade;
Создадим точку 1 сохранения транзакций DEMO@ORCL>SAVEPOINT poin1; Точка отката создана. DEMO@ORCL>DELETE FROM s_grade WHERE grade_id=4; 1 строка удалена. DEMO@ORCL>SELECT * FROM s_grade;
Создадим точку 2 сохранения транзакций DEMO@ORCL>SAVEPOINT poin2; Точка отката создана. DEMO@ORCL>DELETE FROM s_grade; 3 строк удалено. DEMO@ORCL>SELECT * FROM s_grade; Строки не выбраны Вернемся к точке сохранения транзакции DEMO@ORCL>ROLLBACK TO poin2; Откат завершен DEMO@ORCL>SELECT * FROM s_grade;
62
DEMO@ORCL>ROLLBACK TO poin1; Откат завершен DEMO@ORCL>SELECT * FROM s_grade;
DEMO@ORCL>commit; II.5. Создание и изменение структуры таблиц средствами DDL Q5_1. Создание таблицы department1 и заполнение ее данными: DEMO@ORCL>CREATE TABLE department1( department_id NUMBER(2,0) NOT NULL, name VARCHAR2(14) NULL, location_id NUMBER(3,0) NULL, CHECK (department_id IS NOT NULL), PRIMARY KEY (department_id), FOREIGN KEY (location_id) REFERENCES location(location_id) ); Таблица создана. DEMO@ORCL>INSERT INTO department1 (SELECT * FROM department); 13 строк создано. Создание таблицы employee1 заполнение ее данными: DEMO@ORCL>CREATE TABLE employee1( employee_id NUMBER(4,0) NOT NULL, last_name VARCHAR2(15) , first_name VARCHAR2(15) , middle_initial VARCHAR2(1), job_id NUMBER(3,0), manager_id NUMBER(4,0), hire_date DATE, salary NUMBER(7,2), commission NUMBER(7,2), department_id NUMBER(2,0) NOT NULL, CHECK (department_id BETWEEN 10 AND 99) DISABLE, PRIMARY KEY (employee_id), FOREIGN KEY (job_id) REFERENCES job(job_id),
63
FOREIGN KEY (manager_id) REFERENCES employee(employee_id), FOREIGN KEY (department_id) REFERENCES department1(department_id), CONSTRAINT lst_name_dept_id UNIQUE(last_name,department_id) ); Таблица создана. DEMO@ORCL>INSERT INTO employee1 (SELECT * FROM employee); 32 строк создано. В созданной таблице один столбец (employee_id) является primary key и три столбца (job_id, manager_id, department_id) образуют три разных foreign key по одному столбцу в каждом. Кроме того определено ограничение целостности (constraint) lst_name_dept_id, требующее уникальности фамилии служащего в отделе. Столбцы, для которых указано «NOT NULL», должны быть заполнены информацией при вставке строк в таблицу. Ограничение CHECK задает диапазон значений department_id от 10 до 99. Это ограничение отключено (…DISABLE). Q5_2. Добавим новые столбцы в таблицу employee1 DEMO@ORCL>ALTER TABLE employee1 ADD birth_day DATE; Таблица изменена. DEMO@ORCL>ALTER TABLE employee1 ADD E_SEX CHAR(1) DEFAULT ('M'); Q5_3. Удалим столбцы из таблицы employee1 DEMO@ORCL>ALTER TABLE employee1 DROP COLUMN middle_initial; Таблица изменена. DEMO@ORCL>ALTER TABLE employee1 DROP COLUMN commission; Таблица изменена. DEMO@ORCL>DESC employee1 Имя Пусто? ------------------------------------ -------EMPLOYEE_ID NOT NULL LAST_NAME FIRST_NAME E_SEX JOB_ID MANAGER_ID HIRE_DATE SALARY DEPARTMENT_ID NOT NULL BIRTH_DAY NOT NULL
Тип -------------NUMBER(4) VARCHAR2(15) VARCHAR2(15) CHAR(1) NUMBER(3) NUMBER(4) DATE NUMBER(7,2) NUMBER(2) DATE
Q5_4. Добавим ограничение целостности к столбцу e_sex DEMO@ORCL>ALTER TABLE employee1 ADD CONSTRAINT check_sex check(e_sex in ('M','F')); Таблица изменена. Q5_5. Задействуем отключенное при создании таблицы employee1 ограничение целостности для столбца department_id, которым устанавливается диапазон значений от 10 до 99. Для такого задействования нам надо определить имя этого ограничения,
64
которое Oracle присваивает автоматически при создании таблицы. Для этого извлечем данные из таблицы словаря Oracle, предварительно отформатировав один из ее столбцов: DEMO@ORCL>COL search_condition FORMAT A40 DEMO@ORCL>SELECT constraint_name, search_condition, status FROM user_constraints WHERE table_name='EMPLOYEE1';
Теперь можно «включить» ограничение SYS_C006046 (как видно из результата запроса, ограничению с таким именем соответствует фраза «DEPARTMENT_ID BETWEEN 10 AND 99»): DEMO@ORCL>ALTER TABLE employee1 MODIFY CONSTRAINT SYS_C006046 ENABLE; Таблица изменена Чтобы убедиться, что ограничение включено, повторим запрос к словарю: DEMO@ORCL>SELECT constraint_name, search_condition, status FROM user_constraints WHERE table_name='EMPLOYEE1';
Q5_6. Удалим добавленное ограничение целостности check_sex DEMO@ORCL>ALTER TABLE employee1 DROP CONSTRAINT check_sex; Таблица изменена. Q5_7. Скорректируем максимальную длину столбца last_name DEMO@ORCL>ALTER TABLE employee1 MODIFY (last_name VARCHAR2(30)); Таблица переименована. Q5_8. Переименование таблицы DEMO@ORCL>RENAME employee1 TO employee2; Таблица переименована. Q5_9. Переименование столбца таблицы employee2 DEMO@ORCL>ALTER TABLE employee2 RENAME COLUMN e_sex TO sex; Таблица изменена.
65
Q5_10. Удалим таблицы department1, employee2: DEMO@ORCL>DROP TABLE department1; DROP TABLE department1 * ошибка в строке 1: ORA-02449: уникальный/первичный ключ в таблице, на которую ссылаются по внешнему ключу На таблицу department1 ссылаются записи таблицы employee2 (переименованной таблицы EMPLOYEE1), поэтому команду удаления таблицы DEPARTMENT1 надо писать иначе: DEMO@ORCL>DROP TABLE department1 CASCADE CONSTRAINT; Таблица удалена. DEMO@ORCL>DROP TABLE employee2; Таблица удалена. Q5_11. Опция EXCEPTION INTO Создадим таблицу, которую будем использовать для занесения информации о строках, нарушающих ограничение целостности DEMO@ORCL> CREATE TABLE my_exceptions ( row_id ROWID, owner VARCHAR2(30), table_name VARCHAR2(30), constraint VARCHAR2(30)); Нарушать ограничение целостности будем для таблицы s_grade. Текущее ее содержимое: DEMO@ORCL>SELECT * FROM s_grade;
Продублируем информацию в ней: DEMO@ORCL> INSERT INTO s_grade (SELECT * FROM s_grade); 4 строк создано. DEMO@ORCL>SELECT * FROM s_grade;
Видно, что в столбце grade_id значения повторяются. Скорректируем таблицу s_grade, сделаем попытку определить столбец grade_id, как primary key. Часть команды «…exseption into my_exseption» указывает Oracle, куда помещать информацию о тех строках таблицы, из-за которых корректировка невозможна, т.е. столбец grade_id не удается определить, как primary key.
66
DEMO@ORCL>alter table S_GRADE add constraint pk_grade_id primary key(grade_id) exceptions into MY_EXCEPTIONS; alter table S_GRADE add constraint pk_grade_id primary key(grade_id) * ошибка в строке 1: ORA-02437: невозможно подтвердить (DEMO.PK_GRADE_ID) - нарушен первичный ключ Так как значения столбца grade_id повторяются, выполнить команду невозможно, Oracle выдает сообщение об ошибке и помещает в таблицу MY_EXSEPTION строки, из-за которых эта ошибка появляется. DEMO@ORCL>SELECT * FROM my_exceptions;
Q5_12. Тип LONG в документации Oracle определяется устаревшим типом и оставляется в целях использования для обратной совместимости с прежними версиями Oracle. Таблица user_constraints, из которой мы выше извлекали информацию имеет столбец search_condition типа LONG. Теперь попробуем извлечь из таблицы user_constraints информацию, воспользовавшись условием WHERE для столбца search_condition типа LONG: DEMO@ORCL>SELECT * FROM user_constraints WHERE search_condition LIKE '%department%'; WHERE search_condition LIKE '%department%' * ошибка в строке 2: ORA-00932: несовместимые типы данных: ожидается NUMBER, получено LONG. Причем функции преобразования типов ( TO_LOB, TO_CHAR и т.д.) здесь не помогают. Прямое преобразование типа LONG в тип CLOB не проходит, так как во-первых user_constraints является представлением словаря, а во-вторых структуру объектов словаря, «хозяйства» администратора базы данных менять не рекомендуется, да и привилегии на это нужны особые, которых у пользователя demo нет: DEMO@ORCL>ALTER TABLE user_constraints MODIFY (search_condition CLOB); alter table user_constraints modify (search_condition CLOB) * ошибка в строке 1: ORA-00942: таблица или представление пользователя не существует Для того чтобы запросы вышеприведенного типа обрабатывались (SELECT * FROM user_constraints WHERE search_condition LIKE '%department%';), возможен такой вариант преобразования типа LONG в тип CLOB: DEMO@ORCL>CREATE TABLE my_user_constraints ( constraint_name VARCHAR2(30),
67
table_name VARCHAR2(30), search_condition CLOB); Таблица создана. DEMO@ORCL>INSERT INTO my_user_constraints (SELECT constraint_name, table_name, TO_LOB(search_condition) FROM user_constraints); 83 строк создано. Без функции преобразования TO_LOB() Oracle не позволяет переносить тип LONG в другой столбец такого же типа или типа CLOB. DEMO@ORCL> INSERT INTO my_user_constraints (SELECT constraint_name, table_name, search_condition FROM user_constraints); (SELECT constraint_name, table_name,search_condition * ошибка в строке 2: ORA-00997: неверное использование типа данных LONG Теперь тот запрос, который на столбце типа LONG выдавал ошибку, со столбцом типа CLOB проходит: DEMO@ORCL>SELECT * FROM my_user_constraints WHERE search_condition LIKE '%department%';
II.6. Другие объекты базы данных Q6_1. Создание представления DEMO@ORCL>CREATE OR REPLACE VIEW emp_dept AS SELECT e.employee_id, e.last_name, department_id, d.name AS department FROM employee e join department d USING (department_id) WHERE department_id=10; Представление создано. Вставка значений в это представление не получится. Нельзя изменить более одной таблицы посредством связанного представления. Если мы попытаемся вставить только один столбец одной таблицы (employee_id), Oracle потребует вставки foreign key
68
(department_id) и т.д. для всех вариантов вставки столбцов. Изменение в одном варианте (UPDATE emp_dept SET employee_id WHERE employee_id…..) возможно, но только для строк служащего, у которого нет подчиненных. Q6_2. Создание представления с опцией READ ONLY, запрещающей insert, update, delete с базовой таблицей, на основе которой создано представление, через это представление DEMO@ORCL>CREATE OR REPLACE VIEW emp_v1 AS SELECT employee_id,last_name, (salary+NVL(commission, 0)) AS "Общая зарплата_отдела", department_id FROM employee WITH READ ONLY; Q6_3. Использование представлений для отработки запроса Использование представлений для отработки запроса нередко «спасает» студентов на контрольной работе по SQL. Рассмотрим такой запрос: «Получите имена всех заказчиков товара, которые оформляли заказы два или более раза в 1990 году, год заказа, а также фамилии торговых представителей для этих заказчиков. Выводимый список упорядочить по имени заказчика и году продаж». Посмотрим, как этот запрос можно отработать с использованием представлений. Отметим, что: идентификатор заказчика - customer.custmer_id присутствует также в качестве FOREIGN KEY и в таблице sales_order; имя заказчика - customer.name; год заказа – TO_CHAR(sales_order.ship_date,'YYYY'); фамилия торгового представителя – employee.last_name. Сначала найдем имена заказчиков, которые оформляли заказы два или более раза: DEMO@ORCL>CREATE or replace view v1 as SELECT customer_id FROM (SELECT customer_id, ship_date, COUNT(customer_id) AS count FROM sales_order WHERE TO_CHAR(ship_date,'YYYY') = 1989 GROUP BY customer_id) WHERE count>1; DEMO@ORCL>SELECT * FROM v1;
Теперь можно сформировать результирующий запрос. DEMO@ORCL>SELECT DISTINCT c.name, TO_CHAR(so.ship_date,'YYYY') AS year , e.last_name FROM customer c, sales_order so, employee e WHERE c.customer_id=so.customer_id AND c.salesperson_id=e.employee_id AND e.job_id=(SELECT job_id FROM job WHERE function='SALESPERSON')
69
AND c.customer_id IN (select customer_id FROM v1) AND TO_CHAR(so.ship_date,'YYYY') = 1989 order by c.name, year; Осталось освободиться от v1 в результирующем запросе. DEMO@ORCL>SELECT DISTINCT c.name, TO_CHAR(so.ship_date,'YYYY') AS year , e.last_name FROM customer c,sales_order so,employee e WHERE c.customer_id=so.customer_id AND c.salesperson_id=e.employee_id AND e.job_id=(SELECT job_id FROM job WHERE function='SALESPERSON') AND c.customer_id IN ( SELECT customer_id FROM (SELECT customer_id, ship_date, COUNT(customer_id) AS count FROM sales_order WHERE TO_CHAR(ship_date,'YYYY') = 1989 GROUP BY customer_id) WHERE count>1) AND TO_CHAR(so.ship_date,'YYYY') = 1989 ORDER BY c.name, year;
Q6_4. Удалим представление emp_dept. DEMO@ORCL>DROP VIEW emp_dept; Представление удалено. Q6_5. Создадим индекс по столбцу regional_group таблицы location DEMO@ORCL>CREATE INDEX i_location_name ON location(regional_group); Индекс создан. Теперь запросы к таблице location могут выполняться с использованием этого индекса, если в условии запроса используется столбец regional_group, например DEMO@ORCL>SELECT * FROM location WHERE regional_group like 'N%'; Q6_6. Удаление индексов DEMO@ORCL> DROP INDEX i_location_name; Индекс удален. Q6_7. Создадим кластер для таблиц location, department DEMO@ORCL>CREATE CLUSTER loc_dept (location_id NUMBER(3)) storage (initial 100k next 50k); Кластер создан. DEMO@ORCL>CREATE TABLE department1( department_id NUMBER(2) NOT NULL,
70
name VARCHAR2(13), location_id NUMBER(3), CONSTRAINT department_id_pk PRIMARY KEY (department_id)) CLUSTER loc_dept(location_id); Таблица создана. В отличие от создания обычной таблицы при создании таблицы кластера указывается, к какому кластеру таблица принадлежит и по какому столбцу построен кластер. DEMO@ORCL>CREATE TABLE location1( location_id NUMBER(3) NOT NULL, regional_group VARCHAR2(20), CONSTRAINT location_id_pk PRIMARY KEY (location_id)) CLUSTER loc_dept(location_id); Таблица создана. Создаем индекс кластера: DEMO@ORCL>CREATE INDEX idx_loc_dept ON CLUSTER loc_dept; Индекс создан. Заполним таблицы данными: DEMO@ORCL>INSERT INTO department1 (SELECT * FROM department); 11 строк создано. DEMO@ORCL>INSERT INTO location1 (SELECT * FROM location); 4 строк создано. Теперь при отработке запросов, включающих во фразе «…FROM» обе таблицы кластера, Oracle будет отрабатывать эти запросы, извлекая данные из струкутуры кластера. Q6_8. Удаление кластера, всех таблиц, принадлежащих кластеру и всех ограничений целостности, принадлежащих ему DEMO@ORCL>DROP CLUSTER loc_dept INCLUDING TABLES CASCADE CONSTRAINTS; Кластер удален. Q6_9. Создадим public синоним для таблицы department: DEMO@ORCL>CREATE PUBLIC SYNONYM dept FOR department; CREATE SYNONYM dept FOR department * ошибка в строке 1: ORA-01031: привилегий недостаточно Сообщение об ошибке указывает об отсутствии у пользователя DEMO привилегии создавать синонимы. Из другого окна SQL*Plus соединимся пользователем SYSTEM (DBA) и выполним этим пользователем команду: CREATE PUBLIC SYNONYM dept for demo.department;. Теперь к таблице DEPARTMENT пользователю DEMO можно обращаться по более короткому имени синонима: DEMO@ORCL>SELECT * FROM dept;
71
Пользователь DEMO может дать другим пользователям привилегию для работы с таблицей своей схемы: DEMO@ORCL>GRANT select ON employee TO scott; Привилегии предоставлены. Теперь пользователь SCOTT (конечно, предварительно соединившись с Oracle) может извлекать данные из таблицы employee пользователя DEMO такой командой: SELECT * FROM demo.employee; , указав владельца таблицы и название таблицы. Пользователю SCOTT обращаться к таблице EMPLOYEE пользователя DEMO, каждый раз набирая имя пользователя DEMO, не совсем удобно (лишние действия на клавиатуре). Чтобы устранить это неудобство, администратор SYSTEM выполняет команду: CREATE public synonym empl for demo.employee;, после которой и пользователь DEMO, и все другие пользователи, у которых есть привилегия извлекать данные из таблицы EMPLOYEE, принадлежащей пользователю DEMO, могут извлекать данные из этой таблицы, обращаясь к ней по имени EMPL: DEMO@ORCL>SELECT * FROM empl; SCOTT@ORCL>SELECT * FROM empl; Q6_10. Создадим снимок для таблицы SALES_ORDER: DEMO@ORCL>CREATE SNAPSHOT sale_ord AS SELECT customer_id, TO_CHAR(ship_date, 'YYYY') AS year, SUM(total) FROM sales_order GROUP BY (customer_id, TO_CHAR(ship_date, 'YYYY')); FROM SALES_ORDER * ошибка в строке 3: ORA-01031: привилегий недостаточно Пользователь SYSTEM из своей сессии выполняет команду: grant CREATE snapshot to demo;, после чего пользователь DEMO повторяет: DEMO@ORCL>CREATE SNAPSHOT sale_ord AS SELECT customer_id, TO_CHAR(ship_date,'YYYY') AS year, SUM(total) FROM sales_order WHERE TO_CHAR(ship_date,'YYYY')='1989' GROUP BY (customer_id, TO_CHAR(ship_date,'YYYY')) ORDER BY customer_id; Материализованное представление создано.
72
DEMO@ORCL>SELECT * FROM sale_ord;
Q6.11. Удалим созданный снимок: DEMO@ORCL>DROP SNAPSHOT sale_ord; Материализованное представление удалено.
Глава III. Общая последовательность этапов выполнения лабораторных работ по освоению языка PL/SQL Oracle 1. Ознакомление с примерами PL/SQL программ по главам 1-3 и 6-10 книги /2/
Темы этих глав: Введение в PL/SQL (глава 1) Основы PL/SQL (глава 2) Записи и таблицы (глава 3) Курсоры (глава 6) Подпрограммы: процедуры и функции (глава 7) Модули (глава 8) Триггеры (глава 9) Обработка ошибок (глава 10) 2. Написание, отладка и выполнение программ для заданной преподавателем предметной области (такая предметная область была задана для выполнения SQL лабораторных работ), соответствующих программам в вышеприведенных главах книги /2/. Программы из /2/ были после публикации /2/ открыты для общего пользования на сайте издательства «ЛОРИ». Теперь их несложно найти в интернете. Книга /2/ имеется в учебном фонде МИФИ. В нижеприведенной табл. уточняется, что студент должен выполнить в соответствии с образцами программ из /2/. Таблица III.1 № Глава Названия программ Что надо выполнить на лабораторных работах лабо- книги – примеров, (написать на PL/SQL, отладить и рато- /2/ по образцам продемонстрировать в работе ной которых студент должен написать программы для своей предметной области 1 2 3 4
73
1 1
2 1
2
2
3
3
4
6
3 3GL_4GL.SQL, CURSOR.SQL, SIMPLE.SQL, NUMERIC.SQL
4 Для одной из таблиц предметной области написать анонимный блок, предусматривающий использование явного курсора, цикла FOR, вывод информации с помощью DBMS_OUTPUT.PUT_LINE, а также занесение информации в служебную таблицу (типа TEMP_TABLE). Служебную таблицу потребуется создать в своей схеме ALLTHREE.SQL, Для одной из таблиц предметной области написать: ALLTHREE1.SQL, А) хранимую процедуру, использующую курсор, COMMENTS1.SQL, цикл FOR, условие EXITWHEN.SQL, IF…THEN….ELSIF…THEN...ELSE…END IF, FORSCOPE.SQL, вывод информации с помощью GOTO.SQL, DBMS_OUTPUT.PUT_LINE, а также занесение IF1.SQL, If1b.sql, информации в служебную таблицу; NULL.SQL, Б) триггер на вставку, проверяющий некоторое PROC.SQL, ограничение (придумать самостоятельно) на TRIGGER.SQL вставляемую символьную информацию ASSIGN.SQL, Для одной из таблиц предметной области написать COUNT.SQL, анонимный блок, предусматривающий работу с DELETE.SQL, записями и таблицами PL/SQL: заполнение таблиц, EXISTS.SQL, извлечение данных из таблиц, удаление строк FRSTLAST.SQL, таблицы, а также использование в работе с NULLTAB.SQL, таблицами условия IF, простого цикла LOOP и NXTPRIOR.SQL, метода NEXT SELECT.SQL, TABREC.SQL BADFETCH.SQL, Для нескольких таблиц предметной области BINDS.SQL, написать COMMIT1.SQL, анонимный блок и хранимую процедуру, COMMIT2.SQL, использующие явный и неявный курсор. Эти блок CUREXAMP.SQL, и процедура должны предусматривать CURSOR2.SQL, использование связанных переменных в курсоре, FORLOOP.SQL, цикл FOR, извлечение и изменение значений FORUPDAT.SQL, таблицы в этом цикле FOR, использование NOMATCH1.SQL, различных курсорных атрибутов NOMATCH2.SQL, (SQL%NOTFOUND, SQL%ROWCOUNT), SIMPLE.SQL, использование курсорной переменной WHILE.SQL
74
1 5
2 7
3 ADDSTUD.SQL, ALMOSTFL2.SQL, CALLFUNC.SQL, CALLME.SQL, CALLPROC.SQL, CLASINFO.SQL, DEFAULT.SQL, ERROR.SQL, FORWAR.SQL (MUTUAL.SQL по Скотту Урману), FORWARD.SQL, LOCAL2.SQL, LOCAL.SQL, MODETEST1.SQL, RFCLASS2.SQL
4 Для нескольких таблиц предметной области написать: А) функцию, возвращающую символьное или числовое значение; Б) хранимую процедуру, использующую обращение к функции, явный курсор, цикл FOR, локальную процедуру. Текст этого задания можно дополнительно уточнить с преподавателем
6
8
CLPACK.SQL, CLLIST.SQL, My_Studops.sql, OVERLOAD.SQL, RANDOM.SQL, EXPORT.SQL
7
9
UPDATEMS.SQL, INSTEAD.SQL, ORDER.SQL, PSEUDO.SQL, STUDID.SQL, RSCHANGE.SQL, RSINSERT.SQL, LIMMAJOR.SQL, MUTATING.SQL, Demobld.sql
8
10
Выполнить все скрипты главы 10 и задать вопросы преподавателю по непонятным моментам
Скомпилировать модуль export.sql для своей схемы (настройку файла инициализационных параметров - установку параметра utl_file_dir и задействование его, выдачу объектной привилегии execute на sys.utl_file владельцу схемы, из которой будет выполняться экспорт, выполнит преподаватель) и проверить его работу. Откомпилировать модуль spelchek.sql из главы 5 и проверить его в работе, для чего предварительно создать и заполнить таблицу s_emp (скрипт s_emp.sql в главе 5). Примеры для проверки работы взять со с. 123-125 книги /2/ В своей схеме создать для одной из таблиц триггер на вставку, изменение и удаление данных и продемонстрировать его в работе. Создать представление для одной из таблиц по типу представления на с.208 книги /2/ и для этого представления создать триггер instead of. Создать триггер с использованием :new, :old и триггерных предикатов INSERTING, UPDATING, DELETING. Проверить в работе каскадное изменение данных (выполнить скрипт Demobld.sql и проверить его работу для таблицы DEPT по примеру на с.223 книги /2/) Написать хранимую процедуру с несколькими (!) exseption
75
При выполнении лабораторных работ студенту может потребоваться выполнить корректировку «своей» предметной области (добавить столбец/столбцы в таблицу, определить его/их внешним ключем, заполнить его/их; добавить новую таблицу/таблицы и заполнить ее/их; заполнить новыми данными ранее существовавшую/существовавшие таблицу/таблицы и т.д.). Все эти корректировки должны быть скомпонованы в отдельном скрипте, который, как и spol –файлы лабораторных работ, предъявляется преподавателю при сдаче лабораторных работ. Лабораторные работы могут выполняться студентом вне
дисплейного класса, а на занятиях предъявляться преподавателю для их сдачи. При сдаче преподаватель может предложить студенту скорректировать сдаваемые программы для выполнения предлагаемых преподавателем дополнительных действий по обработке информации предметной области. В главе I уже говорилось о том, что график выполнения лабораторных работ по освоению SQL и PL/SQL предусматривает длительность аудиторной лабораторной работы для каждого студента - четыре академических часа раз в две недели. Поэтому, начиная с девятой недели (сразу после контрольной по SQL), на каждом из двух – трех занятий во второй половине семестра студент должен сдавать преподавателю программы, написанные в соответствии с образцами из трех пяти глав табл. III.1, чтобы к контрольной работе по PL/SQL в конце семестра выполнить задания по всем восьми лабораторным из данной таблицы. Библиографический список 1. Юринский В., Бачин А., Абрамов В. Oracle 7. Практическое руководство.– М.: SoftService, 1997 2. Урман С. Программирование на языке PL/SQL.- М.: ЛОРИ, 1999. 3. Мишра Санжей, Бьюли Алан. Секреты Oracle SQL. – СПБ – М.: СимволПлюс, 2003 4. Документация по Oracle10g 5. Примеры задач SQL олимпиады 2007г. http://gpi-mpei.ru//content/blogcategory/72/241/
76
ПРИЛОЖЕНИЯ Приложение 1. Скрипт demo.sql создания предметной области пользователя DEMO SET ECHO ON SET TERMOUT ON DROP TABLE customer CASCADE CONSTRAINT; DROP TABLE department CASCADE CONSTRAINT; DROP TABLE employee CASCADE CONSTRAINT; DROP TABLE item CASCADE CONSTRAINT; DROP TABLE job CASCADE CONSTRAINT; DROP TABLE location CASCADE CONSTRAINT; DROP TABLE price CASCADE CONSTRAINT; DROP TABLE product CASCADE CONSTRAINT; DROP TABLE salary_GRADE CASCADE CONSTRAINT; DROP TABLE sales_order CASCADE CONSTRAINT; ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-YYYY'; ALTER SESSION SET NLS_LANGUAGE='AMERICAN'; CREATE TABLE job( job_id NUMBER(3,0) NOT NULL, function VARCHAR2(30) NULL, CHECK (job_id IS NOT NULL), CHECK (function IS NOT NULL), PRIMARY KEY (job_id) ); INSERT INTO job VALUES( INSERT INTO job VALUES( INSERT INTO job VALUES( INSERT INTO job VALUES( INSERT INTO job VALUES( INSERT INTO job VALUES(
667 ,'CLERK'); 668 ,'STAFF'); 669 ,'ANALYST'); 670 ,'SALESPERSON'); 671 ,'MANAGER'); 672 ,'PRESIDENT');
CREATE TABLE location( location_id NUMBER(3,0) NOT NULL, regional_group VARCHAR2(20) NULL, CHECK (location_id IS NOT NULL), PRIMARY KEY (location_id) ); INSERT INTO location VALUES( INSERT INTO location VALUES( INSERT INTO location VALUES( INSERT INTO location VALUES( INSERT INTO location VALUES( INSERT INTO location VALUES(
122 , NEW YORK'); 124 , 'DALLAS'); 123 , 'CHICAGO'); 167 , 'BOSTON'); 168 , 'MOSCOW'); 169 , 'KIEV');
CREATE TABLE department( department_id NUMBER(2,0) NOT NULL, name VARCHAR2(14) NULL, location_id NUMBER(3,0) NULL, CHECK (department_id IS NOT NULL), PRIMARY KEY (department_id), FOREIGN KEY (location_id) REFERENCES location(location_id) ); INSERT INTO department VALUES( INSERT INTO department VALUES(
10 , 'ACCOUNTING', 20 , 'RESEARCH',
122 ); 124 );
77 INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES( INSERT INTO department VALUES(
30 , 'SALES', 40 , OPERATIONS', 12 , 'RESEARCH', 13 , 'SALES', 14 , 'OPERATIONS', 23 , 'SALES', 24 , 'OPERATIONS', 34 , 'OPERATIONS', 43 , 'SALES', 55 , 'OPERATIONS', 56 , 'SALES',
123 ); 167 ); 122 ); 122 ); 122 ); 124 ); 124 ); 123 ); 167 ); NULL ); NULL );
CREATE TABLE employee( employee_id NUMBER(4,0) NOT NULL, last_name VARCHAR2(15) NULL, first_name VARCHAR2(15) NULL, middle_initial VARCHAR2(1) NULL, job_id NUMBER(3,0) NULL, manager_id NUMBER(4,0) NULL, hire_date DATE NULL, salary NUMBER(7,2) NULL, commission NUMBER(7,2) NULL, department_id NUMBER(2,0) NULL, CHECK (employee_id IS NOT NULL), CHECK (department_id IS NOT NULL), PRIMARY KEY (employee_id), FOREIGN KEY (job_id) REFERENCES job(job_id), FOREIGN KEY (manager_id) REFERENCES employee(employee_id), FOREIGN KEY (department_id) REFERENCES department(department_id) );
INSERT INTO employee VALUES(7839,'KING', 'FRANCIS', 'A',672,NULL,'17-NOV-1985',5000,NULL,10); INSERT INTO employee VALUES(7505,'DOYLE', 'JEAN', 'K',671,7839,'04-APR-1985',2850,NULL,13); INSERT INTO employee VALUES(7555,'PETERS', 'DANIEL', 'T',670,7505,'31-MAR-1985',1250,300, 13); INSERT INTO employee VALUES(7557,'SHAW', 'KAREN', 'P',670,7505,'02-APR-1985',1250,1200,13); INSERT INTO employee VALUES(7600,'PORTER', 'RAYMOND', 'Y',670,7505,'15-APR-1985',1250,900, 13); INSERT INTO employee VALUES(7820,'ROSS', 'PAUL', 'S',670,7505,'01-JUN-1985',1300,800, 13); INSERT INTO employee VALUES(7950,'JENSEN', 'ALICE', 'B',667,7505,'15-JAN-1987', 750,NULL,13); INSERT INTO employee VALUES(7506,'DENNIS', 'LYNN', 'S',671,7839,'15-MAY-1985',2750,NULL,23); INSERT INTO employee VALUES(7560,'DUNCAN', 'SARAH', 'S',670,7506,'31-MAY-1985',1250,NULL,23); INSERT INTO employee VALUES(7564,'LANGE', 'GREGORY', 'J',670,7506,'01-JUN-1985',1250,300, 23); INSERT INTO employee VALUES(7789,'WEST', 'LIVIA', 'N',670,7506,'04-APR-1985',1500,1000,23); INSERT INTO employee VALUES(7954,'MURRAY', 'JAMES', 'T',667,7506,'16-JAN-1987', 750,NULL,23); INSERT INTO employee VALUES(7507,'BAKER', 'LESLIE', 'D',671,7839,'10-JUN-1985',2200,NULL,14); INSERT INTO employee VALUES(7609,'LEWIS', 'RICHARD', 'M',668,7507,'16-APR-1985',1800,NULL,14); INSERT INTO employee VALUES(7676,'SOMMERS','DENISE', 'D',668,7507,'19-APR-1985',1850,NULL,14); INSERT INTO employee VALUES(7566,'JONES', 'TERRY', 'M',671,7839,'02-APR-1985',2975,NULL,20); INSERT INTO employee VALUES(7788,'SCOTT', 'DONALD', 'T',669,7566,'09-DEC-1986',3000,NULL,20); INSERT INTO employee VALUES(7876,'ADAMS', 'DIANE', 'G',667,7788,'12-JAN-1987',1100,NULL,20); INSERT INTO employee VALUES(7902,'FORD', 'JENNIFER','D',669,7566,'03-DEC-1985',3000,NULL,20); INSERT INTO employee VALUES(7369,'SMITH', 'JOHN', 'Q',667,7902,'17-DEC-1984', 800,NULL,20); INSERT INTO employee VALUES(7569,'ALBERTS','CHRIS', 'L',671,7839,'06-APR-1985',3000,NULL,12); INSERT INTO employee VALUES(7799,'FISHER', 'MATTHEW', 'G',669,7569,'12-DEC-1986',3000,NULL,12); INSERT INTO employee VALUES(7919,'DOUGLAS','MICHAEL', 'A',667,7799,'04-JAN-1987', 800,NULL,12); INSERT INTO employee VALUES(7916,'ROBERTS','GRACE', 'M',669,7569,'04-JAN-1987',2875,NULL,12); INSERT INTO employee VALUES(7698,'BLAKE', 'MARION', 'S',671,7839,'01-MAY-1985',2850,NULL,30); INSERT INTO employee VALUES(7499,'ALLEN', 'KEVIN', 'J',670,7698,'20-FEB-1985',1600,300, 30); INSERT INTO employee VALUES(7521,'WARD', 'CYNTHIA', 'D',670,7698,'22-FEB-1985',1250,500, 30);
78 INSERT INTO employee VALUES(7654,'MARTIN', 'KENNETH', 'J',670,7698,'28-SEP-1985',1250,1400,30); INSERT INTO employee VALUES(7844,'TURNER', 'MARY', 'A',670,7698,'08-SEP-1985',1500,NULL,30); INSERT INTO employee VALUES(7900,'JAMES', 'FRED', 'S',667,7698,'03-DEC-1985', 950,NULL,30); INSERT INTO employee VALUES(7782,'CLARK', 'CAROL', 'F',671,7839,'09-JUN-1985',2450,NULL,10); INSERT INTO employee VALUES(7934,'MILLER', 'BARBARA', 'M',667,7782,'23-JAN-1986',1300,NULL,10);
CREATE TABLE customer( customer_id NUMBER(6,0) NOT NULL, name VARCHAR2(20) NULL, address VARCHAR2(21) NULL, city VARCHAR2(16) NULL, state VARCHAR2(2) NULL, zip_code VARCHAR2(9) NULL, area_code NUMBER(3,0) NULL, phone_number NUMBER(7,0) NULL, salesperson_id NUMBER(4,0) NULL, credit_limit NUMBER(9,2) NULL, CHECK (customer_id IS NOT NULL), CHECK (salesperson_id IS NOT NULL), CHECK (customer_id > 0), CHECK (state = UPPER(state)), CHECK (LENGTH(NVL(zip_code, '99999')) IN (5, 9)), PRIMARY KEY (customer_id), FOREIGN KEY (salesperson_id) REFERENCES employee(employee_id) );
INSERT INTO customer VALUES(100,'JOCKSPORTS', '345 VIEWRIDGE', 'BELMONT', 'CA','96711',415,5986609,7844,5000); INSERT INTO customer VALUES(101,'TKB SPORT SHOP', '490 BOLI RD.', 'REDWOOD CITY', 'CA','94061',415,3681223,7521,10000); INSERT INTO customer VALUES(102,'VOLLYRITE', '9722 HAMILTON', 'BURLINGAME', 'CA','95133',415,6443341,7654,7000); INSERT INTO customer VALUES(103,'JUST TENNIS', 'HILLVIEW MALL', 'BURLINGAME', 'CA','97544',415,6779312,7521,3000); INSERT INTO customer VALUES(104,'EVERY MOUNTAIN', '574 SURRY RD.', 'CUPERTINO', 'CA','93301',408,9962323,7499,10000); INSERT INTO customer VALUES(105,'K + T SPORTS', '3476 EL PASEO', 'SANTA CLARA', 'CA','91003',408,3769966,7844,5000); INSERT INTO customer VALUES(106,'SHAPE UP', '908 SEQUOIA', 'PALO ALTO', 'CA','94301',415,3649777,7521,6000); INSERT INTO customer VALUES(107,'WOMENS SPORTS', 'VALCO VILLAGE', 'SUNNYVALE', 'CA','93301',408,9674398,7499,10000); INSERT INTO customer VALUES(108,'NORTH WOODS HEALTH', '98 LONE PINE WAY', 'HIBBING', 'MN','55649',612,5669123,7844,8000); INSERT INTO customer VALUES(201,'STADIUM SPORTS', '47 IRVING PL.', 'NEW YORK', 'NY','10003',212,5555335,7557,10000); INSERT INTO customer VALUES(202,'HOOPS', '2345 ADAMS AVE.', 'LEICESTER', 'MA','01524',508,5557542,7820,5000); INSERT INTO customer VALUES(203,'REBOUND SPORTS', '2 E. 14TH ST.', 'NEW YORK', 'NY','10009',212,5555989,7557,10000); INSERT INTO customer VALUES(204,'THE POWER FORWARD', '1 KNOTS LANDING', 'DALLAS', 'TX','75248',214,5550505,7560,12000); INSERT INTO customer VALUES(205,'POINT GUARD', '20 THURSTON ST.', 'YONKERS', 'NY','10956',914,5554766,7557,3000); INSERT INTO customer VALUES(206,'THE COLISEUM', '5678 WILBUR PL.', 'SCARSDALE', 'NY','10583',914,5550217,7557,6000); INSERT INTO customer VALUES(207,'FAST BREAK', '1000 HERBERT LN.', 'CONCORD', 'MA','01742',508,5551298,7820,7000); INSERT INTO customer VALUES(208,'AL AND BOB''S SPORTS','260 YORKTOWN CT.', 'AUSTIN', 'TX','78731',512,5557631,7560,4000);
79 INSERT INTO customer VALUES(211,'AT BAT', '234 BEACHEM ST.', 'BROOKLINE', 'MA','02146',617,5557385,7820,8000); INSERT INTO customer VALUES(212,'ALL SPORT', '1000 38TH ST.', 'BROOKLYN', 'NY','11210',718,5551739,7600,6000); INSERT INTO customer VALUES(213,'GOOD SPORT', '400 46TH ST.', 'SUNNYSIDE', 'NY','11104',718,5553771,7600,5000); INSERT INTO customer VALUES(214,'AL''S PRO SHOP', '45 SPRUCE ST.', 'SPRING', 'TX','77388',713,5555172,7564,8000); INSERT INTO customer VALUES(215,'BOB''S FAMILY SPORTS','400 E. 23RD', 'HOUSTON', 'TX','77026',713,5558015,7654,8000); INSERT INTO customer VALUES(216,'THE ALL AMERICAN', '547 PRENTICE RD.', 'CHELSEA', 'MA','02150',617,5553047,7820,5000); INSERT INTO customer VALUES(217,'HIT, THROW, AND RUN', '333 WOOD COURT', 'GRAPEVINE', 'TX','76051',817,5552352,7564,6000); INSERT INTO customer VALUES(218,'THE OUTFIELD', '346 GARDEN BLVD.', 'FLUSHING', 'NY','11355',718,5552131,7820,4000); INSERT INTO customer VALUES(221,'WHEELS AND DEALS', '2 MEMORIAL DRIVE', 'HOUSTON', 'TX','77007',713,5554139,7789,10000); INSERT INTO customer VALUES(222,'JUST BIKES', '4000 PARKRIDGE BLVD.','DALLAS', 'TX','75205',214,5558735,7789,4000); INSERT INTO customer VALUES(223,'VELO SPORTS', '23 WHITE ST.', 'MALDEN', 'MA','02148',617,5554983,7820,5000); INSERT INTO customer VALUES(224,'JOE''S BIKE SHOP', '4500 FOX COURT', 'GRAND PRARIE', 'TX','75051',214,5559834,7789,6000); INSERT INTO customer VALUES(225,'BOB''S SWIM, AND RUN','300 HORSECREEK', 'IRVING', 'TX','75039',214,5558388,7789,7000); INSERT INTO customer VALUES(226,'CENTURY SHOP', '8 DAGMAR DR.', 'HUNTINGTON', 'NY','11743',516,5553006,7555,4000); INSERT INTO customer VALUES(227,'THE TOUR', '2500 GARDNER RD.', 'SOMERVILLE', 'MA','02144',617,5556673,7820,5000); INSERT INTO customer VALUES(228 ,'FITNESS FIRST', '5000 85TH ST.', 'JACKSON HEIGHTS','NY','11372',718,5558710,7555,4000);
CREATE TABLE sales_order( order_id NUMBER(4,0) NOT NULL, order_date DATE NULL, customer_id NUMBER(6,0) NULL, ship_date DATE NULL, total NUMBER(8,2) NULL, CHECK (order_id IS NOT NULL), CHECK (customer_id IS NOT NULL), CHECK (total >= 0), PRIMARY KEY (order_id), FOREIGN KEY (customer_id) REFERENCES customer(customer_id) );
INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES(
610 , '07-JAN-1991' , 611 , '11-JAN-1991' , 612 , '15-JAN-1991' , 601 , '16-NOV-1990' , 602 , '05-JUN-1990' , 600 , '01-MAY-1990' , 604 , '15-JUN-1990' , 605 , '14-JUL-1990' , 606 , '14-JUL-1990' , 609 , '01-AUG-1990' , 607 , '18-JUL-1990' , 608 , '25-JUL-1990' , 603 , '05-JUN-1990' , 620 , '12-MAR-1991' ,
101 , '08-JAN-1991' , 101.4 ); 102 , '11-JAN-1991' , 45 ); 104 , '20-JAN-1991' , 5860 ); 106 , '30-NOV-1990' , 60.8 ); 102 , '20-JUN-1990' , 56 ); 103 , '29-MAY-1990' , 42 ); 106 , '30-JUN-1990' , 642 ); 106 , '30-JUL-1990' , 8374 ); 100 , '30-JUL-1990' , 3.4 ); 100 , '15-AUG-1990' , 102.5 ); 104 , '18-JUL-1990' , 5.6 ); 104 , '25-JUL-1990' , 35.2 ); 102 , '05-JUN-1990' , 224 ); 100 , '12-MAR-1991' , 4450 );
80 INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES(
613 , '01-FEB-1991' , 614 , '01-FEB-1991' , 616 , '03-FEB-1991' , 619 , '22-FEB-1991' , 617 , '05-FEB-1991' , 615 , '01-FEB-1991' , 618 , '15-FEB-1991' , 621 , '15-MAR-1991' , 509 , '04-JUN-1989' , 523 , '13-FEB-1990' , 549 , '27-DEC-1990' , 507 , '26-MAY-1989' , 516 , '30-SEP-1989' , 553 , '02-FEB-1991' , 526 , '04-MAR-1990' , 543 , '04-SEP-1990' , 555 , '04-MAR-1991' , 559 , '16-NOV-1990' , 528 , '24-MAR-1990' , 531 , '31-MAR-1990' , 558 , '31-MAR-1991' , 525 , '28-FEB-1990' , 552 , '02-FEB-1991' , 556 , '10-MAR-1991' , 560 , '25-APR-1991' , 565 , '01-JUN-1991' , 574 , '10-OCT-1991' , 576 , '13-OCT-1991' , 503 , '25-MAR-1989' , 518 , '20-OCT-1989' , 517 , '23-OCT-1989' , 544 , '11-SEP-1990' , 524 , '22-FEB-1990' , 502 , '10-FEB-1989' , 539 , '26-JUN-1990' , 511 , '17-AUG-1989' , 512 , '28-AUG-1989' , 562 , '04-MAY-1991' , 529 , '02-APR-1990' , 563 , '25-MAY-1991' , 568 , '13-JUL-1991' , 504 , '11-APR-1989' , 538 , '23-JUN-1990' , 535 , '28-MAY-1990' , 578 , '19-NOV-1991' , 534 , '11-MAY-1990' , 536 , '21-MAY-1990' , 572 , '19-AUG-1991' , 514 , '05-SEP-1989' , 521 , '20-NOV-1989' , 551 , '22-JAN-1991' , 513 , '06-SEP-1989' , 508 , '26-MAY-1989' , 515 , '19-SEP-1989' , 542 , '11-AUG-1990' , 573 , '10-OCT-1991' , 566 , '09-JUN-1991' , 520 , '16-NOV-1989' , 519 , '20-OCT-1989' , 575 , '04-OCT-1991' , 547 , '16-OCT-1990' , 540 , '15-JUL-1990' ,
108 , '01-FEB-1991' , 6400 ); 102 , '05-FEB-1991' , 23940 ); 103 , '10-FEB-1991' , 764 ); 104 , '04-MAR-1991' , 1260 ); 105 , '03-MAR-1991' , 46370 ); 107 , '06-FEB-1991' , 710 ); 102 , '06-MAR-1991' , 3083 ); 100 , '01-APR-1991' , 730 ); 226 , '10-JUN-1989' , 1174 ); 226 , '13-FEB-1990' , 1165 ); 226 , '14-JAN-1991' , 1620 ); 228 , '27-MAY-1989' , 886 ); 228 , '03-OCT-1989' , 1815 ); 228 , '13-FEB-1991' , 4400 ); 221 , '07-MAR-1990' , 7700 ); 221 , '07-SEP-1990' , 8400 ); 221 , '07-MAR-1991' , 8540 ); 222 , '23-APR-1991' , 387.2 ); 224 , '24-MAR-1990' , 3770 ); 224 , '02-APR-1990' , 1400 ); 224 , '02-APR-1991' , 1700 ); 225 , '03-MAR-1990' , 377 ); 223 , '07-FEB-1991' , 555.8 ); 223 , '12-MAR-1991' , 85 ); 223 , '25-APR-1991' , 72 ); 227 , '03-JUN-1991' , 4900 ); 201 , '12-OCT-1991' , 1685 ); 201 , '18-OCT-1991' , 2058.9 ); 201 , '06-APR-1989' , 1876 ); 201 , '05-NOV-1989' , 2932.5 ); 201 , '07-NOV-1989' , 784 ); 202 , '15-SEP-1990' , 2358 ); 202 , '04-MAR-1990' , 1979 ); 202 , '23-FEB-1989' , 500 ); 202 , '03-JUL-1990' , 1300 ); 202 , '21-AUG-1989' , 647 ); 203 , '13-SEP-1989' , 428 ); 203 , '19-MAY-1991' , 2044.5 ); 203 , '05-APR-1990' , 1264.7 ); 204 , '26-MAY-1991' , 889 ); 204 , '21-JUL-1991' , 1217.4 ); 204 , '12-APR-1989' , 1434.7 ); 204 , '26-JUN-1990' , 741 ); 204 , '09-JUN-1990' , 810 ); 204 , '04-DEC-1991' , 2275.6 ); 206 , '15-MAY-1990' , 420 ); 206 , '06-JUN-1990' , 2135.6 ); 206 , '24-AUG-1991' , 1200.5 ); 207 , '16-SEP-1989' , 1140 ); 207 , '23-NOV-1989' , 896.9 ); 208 , '06-FEB-1991' , 1142 ); 208 , '14-SEP-1989' , 1497 ); 208 , '08-JUN-1989' , 1080 ); 208 , '26-SEP-1989' , 1428.5 ); 208 , '23-AUG-1990' , 2409 ); 201 , '12-OCT-1991' , 1627 ); 201 , '12-JUN-1991' , 3389.2 ); 201 , '19-NOV-1989' , 341.6 ); 201 , '05-NOV-1989' , 955 ); 201 , '17-OCT-1991' , 684 ); 202 , '27-OCT-1990' , 984.4 ); 202 , '17-JUL-1990' , 861.2 );
81 INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES( INSERT INTO sales_order VALUES(
567 , '05-JUL-1991' , 570 , '17-JUL-1991' , 571 , '02-AUG-1991' , 541 , '08-AUG-1990' , 532 , '08-APR-1990' , 527 , '27-FEB-1990' , 501 , '06-JAN-1989' , 564 , '25-MAY-1991' , 537 , '14-JUN-1990' , 522 , '07-JAN-1990' , 554 , '18-FEB-1991' , 569 , '16-JUL-1991' , 550 , '27-JAN-1991' , 548 , '26-NOV-1990' , 505 , '07-APR-1989' , 577 , '06-OCT-1991' , 533 , '28-APR-1990' , 561 , '20-APR-1991' , 506 , '27-APR-1989' , 530 , '03-APR-1990' , 545 , '06-OCT-1990' , 557 , '08-MAR-1991' , 546 , '17-OCT-1990' , 510 , '18-JUL-1989' ,
202 , '07-JUL-1991' , 200 ); 202 , '24-JUL-1991' , 663.6 ); 202 , '12-AUG-1991' , 1095.6 ); 203 , '18-AUG-1990' , 400 ); 203 , '21-APR-1990' , 1295.4 ); 204 , '14-MAR-1990' , 3054.4 ); 204 , '11-JAN-1989' , 216 ); 204 , '26-MAY-1991' , 898.8 ); 204 , '26-JUN-1990' , 672.9 ); 204 , '10-JAN-1990' , 2578.8 ); 204 , '22-FEB-1991' , 282.8 ); 205 , '23-JUL-1991' , 2073 ); 205 , '07-FEB-1991' , 2356 ); 205 , '12-DEC-1990' , 666.9 ); 206 , '22-APR-1989' , 650 ); 206 , '20-OCT-1991' , 1265.75 ); 206 , '02-MAY-1990' , 1122.1 ); 207 , '07-MAY-1991' , 2558.3 ); 208 , '10-MAY-1989' , 2600.4 ); 208 , '07-APR-1990' , 3026.5 ); 208 , '13-OCT-1990' , 475 ); 208 , '12-MAR-1991' , 2461.8 ); 208 , '23-OCT-1990' , 3663 ); 208 , '21-JUL-1989' , 1336.6 );
CREATE TABLE product( product_id NUMBER(6,0) NOT NULL, description VARCHAR2(30) NULL, CHECK (product_id IS NOT NULL), PRIMARY KEY (product_id) );
INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES(
100860 , 'ACE TENNIS RACKET I' ); 100861 , 'ACE TENNIS RACKET II' ); 100870 , 'ACE TENNIS BALLS-3 PACK' ); 100871 , 'ACE TENNIS BALLS-6 PACK' ); 100890 , 'ACE TENNIS NET' ); 101860 , 'SP TENNIS RACKET' ); 101863 , 'SP JUNIOR RACKET' ); 102130 , 'RH: "GUIDE TO TENNIS"' ); 200376 , 'SB ENERGY BAR-6 PACK' ); 200380 , 'SB VITA SNACK-6 PACK' ); 103120 , 'WIFF SOFTBALL BAT I' ); 103121 , 'WIFF SOFTBALL BAT II' ); 103130 , 'WIFF SOFTBALL, SMALL' ); 103131 , 'WIFF SOFTBALL, LARGE' ); 103140 , 'WIFF SOFTBALL MITT (LH)' ); 103141 , 'WIFF SOFTBALL MITT (RH)' ); 102132 , 'RH: "GUIDE TO SOFTBALL"' ); 104350 , 'DUNK BASKETBALL INDOOR' ); 104351 , 'DUNK BASKETBALL OUTDOOR' ); 104352 , 'DUNK BASKETBALL PROFESSIONAL' ); 104360 , 'DUNK HOOP' ); 104361 , 'DUNK HOOP W/FIBERGLASS BOARD' ); 104362 , 'DUNK NETS - RAINBOW' ); 102134 , 'RH: "GUIDE TO BASKETBALL"' ); 105123 , 'YELLOW JERSEY BICYCLE HELMET' ); 105124 , 'YELLOW JERSEY BICYCLE GLOVES' ); 105125 , 'YELLOW JERSEY WATER BOTTLE' ); 105126 , 'YELLOW JERSEY BOTTLE CAGE' );
82 INSERT INTO product VALUES( INSERT INTO product VALUES( INSERT INTO product VALUES(
105127 , 'YELLOW JERSEY FRAME PUMP' 105128 , 'YELLOW JERSEY SADDLE PACK' 102136 , 'RH: "GUIDE TO CYCLING"' );
); );
CREATE TABLE item( order_id NUMBER(4,0) NOT NULL, item_id NUMBER(4,0) NOT NULL, product_id NUMBER(6,0) NULL, actual_price NUMBER(8,2) NULL, quantity NUMBER(8,0) NULL, total NUMBER(8,2) NULL, CHECK (order_id IS NOT NULL), CHECK (item_id IS NOT NULL), PRIMARY KEY (order_id, item_id), FOREIGN KEY (order_id) REFERENCES sales_order(order_id), FOREIGN KEY (product_id) REFERENCES product(product_id) );
INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES(
600 , 610 , 611 , 612 , 601 , 601 , 602 , 604 , 604 , 604 , 603 , 610 , 610 , 613 , 614 , 614 , 612 , 612 , 620 , 620 , 620 , 613 , 613 , 613 , 619 , 617 , 617 , 614 , 616 , 616 , 616 , 616 , 616 , 619 , 619 , 615 , 607 , 615 , 617 , 617 , 617 , 617 , 617 ,
1, 3, 1, 1, 1, 2, 1, 1, 2, 3, 1, 1, 2, 4, 1, 2, 2, 3, 1, 2, 3, 1, 2, 3, 3, 1, 2, 3, 1, 2, 3, 4, 5, 1, 2, 1, 1, 2, 3, 4, 5, 6, 7,
100861 , 100890 , 100861 , 100860 , 200376 , 100860 , 100870 , 100890 , 100861 , 100860 , 100860 , 100860 , 100870 , 200376 , 100860 , 100870 , 100861 , 101863 , 100860 , 200376 , 102130 , 100871 , 101860 , 200380 , 102130 , 100860 , 100861 , 100871 , 100861 , 100870 , 100890 , 102130 , 200376 , 200380 , 200376 , 100861 , 100871 , 100870 , 100870 , 100871 , 100890 , 101860 , 101863 ,
42 , 58 , 45 , 30 , 2.4 , 32 , 2.8 , 58 , 42 , 32 , 32 , 35 , 2.8 , 2.2 , 35 , 2.8 , 40.5 , 10 , 35 , 2.4 , 3.4 , 5.6 , 24 , 4, 3.4 , 35 , 45 , 5.6 , 45 , 2.8 , 58 , 3.4 , 2.4 , 4, 2.4 , 45 , 5.6 , 2.8 , 2.8 , 5.6 , 58 , 24 , 12.5 ,
1, 1, 1, 100 , 12 , 1, 20 , 3, 2, 12 , 7, 1, 3, 200 , 444 , 1000 , 20 , 150 , 10 , 1000 , 500 , 100 , 200 , 150 , 100 , 50 , 100 , 1000 , 10 , 50 , 2, 10 , 10 , 100 , 100 , 4, 1, 100 , 500 , 500 , 500 , 100 , 200 ,
42 ); 58 ); 45 ); 3000 ); 28.8 ); 32 ); 56 ); 174 ); 84 ); 384 ); 224 ); 35 ); 8.4 ); 440 ); 15540 ); 2800 ); 810 ); 1500 ); 350 ); 2400 ); 1700 ); 560 ); 4800 ); 600 ); 340 ); 1750 ); 4500 ); 5600 ); 450 ); 140 ); 116 ); 34 ); 24 ); 400 ); 240 ); 180 ); 5.6 ); 280 ); 1400 ); 2800 ); 29000 ); 2400 ); 2500 );
83 INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES(
617 , 617 , 617 , 609 , 609 , 618 , 618 , 618 , 621 , 621 , 615 , 608 , 608 , 609 , 606 , 605 , 605 , 605 , 605 , 605 , 605 , 612 , 619 , 509 , 509 , 509 , 523 , 523 , 549 , 549 , 549 , 549 , 507 , 507 , 516 , 516 , 516 , 553 , 553 , 526 , 526 , 543 , 543 , 555 , 555 , 555 , 559 , 559 , 559 , 559 , 559 , 528 , 531 , 531 , 558 , 558 , 525 , 552 , 552 , 552 , 552 , 556 ,
8, 9, 10 , 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 1, 1, 1, 2, 3, 4, 5, 6, 4, 4, 1, 2, 3, 1, 2, 1, 2, 3, 4, 1, 2, 1, 2, 3, 1, 2, 1, 2, 1, 2, 1, 2, 3, 1, 2, 3, 4, 5, 1, 1, 2, 1, 2, 1, 1, 2, 3, 4, 1,
102130 , 200376 , 200380 , 100870 , 100890 , 100860 , 100861 , 100870 , 100861 , 100870 , 100871 , 101860 , 100871 , 100861 , 102130 , 100861 , 100870 , 100890 , 101860 , 101863 , 102130 , 100871 , 100871 , 105123 , 105124 , 102136 , 102136 , 105123 , 105123 , 105127 , 105125 , 105124 , 105123 , 105124 , 105124 , 102136 , 105125 , 105127 , 200376 , 105123 , 105124 , 105123 , 105124 , 105123 , 105124 , 102136 , 105123 , 105124 , 105127 , 102136 , 200376 , 105123 , 105124 , 102136 , 105124 , 102136 , 105123 , 105123 , 105124 , 102136 , 200376 , 102136 ,
3.4 , 2.4 , 4, 2.5 , 50 , 35 , 45 , 2.8 , 45 , 2.8 , 5, 24 , 5.6 , 40 , 3.4 , 45 , 2.8 , 58 , 24 , 9.5 , 3.4 , 5.5 , 5.6 , 35 , 9, 3.4 , 3.4 , 37.7 , 38 , 6, 3, 13 , 35.3 , 9, 9, 3.2 , 1.9 , 6, 1.75 , 32 , 9, 34 , 10 , 34 , 10 , 2.8 , 40 , 15 , 6, 3.4 , 2.4 , 37.7 , 11 , 3, 14 , 3, 37.7 , 40 , 15 , 3.4 , 2.4 , 3.4 ,
100 , 200 , 300 , 5, 1, 23 , 50 , 10 , 10 , 100 , 50 , 1, 2, 1, 1, 100 , 500 , 5, 50 , 100 , 10 , 100 , 50 , 30 , 10 , 10 , 10 , 30 , 5, 30 , 200 , 50 , 20 , 20 , 60 , 250 , 250 , 500 , 800 , 100 , 500 , 100 , 500 , 100 , 500 , 50 , 5, 5, 5, 3, 30 , 100 , 100 , 100 , 100 , 100 , 10 , 10 , 10 , 1, 1, 25 ,
340 ); 480 ); 1200 ); 12.5 ); 50 ); 805 ); 2250 ); 28 ); 450 ); 280 ); 250 ); 24 ); 11.2 ); 40 ); 3.4 ); 4500 ); 1400 ); 290 ); 1200 ); 950 ); 34 ); 550 ); 280 ); 1050 ); 90 ); 34 ); 34 ); 1131 ); 190 ); 180 ); 600 ); 650 ); 706 ); 180 ); 540 ); 800 ); 475 ); 3000 ); 1400 ); 3200 ); 4500 ); 3400 ); 5000 ); 3400 ); 5000 ); 140 ); 200 ); 75 ); 30 ); 10.2 ); 72 ); 3770 ); 1100 ); 300 ); 1400 ); 300 ); 377 ); 400 ); 150 ); 3.4 ); 2.4 ); 85 );
84 INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES(
560 , 565 , 565 , 574 , 574 , 576 , 576 , 576 , 576 , 576 , 503 , 503 , 503 , 518 , 518 , 518 , 518 , 518 , 517 , 517 , 544 , 544 , 544 , 544 , 544 , 524 , 524 , 524 , 524 , 502 , 539 , 539 , 511 , 511 , 511 , 512 , 512 , 562 , 562 , 562 , 529 , 529 , 563 , 563 , 568 , 568 , 568 , 504 , 504 , 504 , 538 , 535 , 578 , 578 , 578 , 534 , 536 , 536 , 572 , 572 , 514 , 521 ,
1, 1, 2, 1, 2, 1, 2, 3, 4, 5, 1, 2, 3, 1, 2, 3, 4, 5, 1, 2, 1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 1, 2, 1, 2, 3, 1, 2, 1, 2, 3, 1, 2, 1, 2, 1, 2, 3, 1, 2, 3, 1, 1, 1, 2, 3, 1, 1, 2, 1, 2, 1, 1,
200376 , 105123 , 105124 , 104350 , 200380 , 104350 , 104351 , 104362 , 200376 , 200380 , 104350 , 104351 , 104352 , 104350 , 104351 , 104360 , 104362 , 102134 , 104352 , 102134 , 104350 , 104351 , 104352 , 104361 , 104362 , 104350 , 104351 , 104362 , 102134 , 104352 , 104351 , 104360 , 104351 , 104362 , 102134 , 104360 , 102134 , 104351 , 104352 , 104362 , 104350 , 102134 , 104352 , 200376 , 104351 , 200376 , 200380 , 104350 , 104351 , 104352 , 104351 , 104352 , 104350 , 200376 , 200380 , 104350 , 104350 , 104352 , 104351 , 104352 , 104350 , 104350 ,
2.4 , 37 , 12 , 41.8 , 3.2 , 44 , 26 , 4.25 , 2.16 , 3.4 , 38 , 23.6 , 50 , 38 , 23.6 , 36 , 4.25 , 3.4 , 50 , 3.4 , 44 , 26 , 58.3 , 50 , 4.5 , 42 , 23.56 , 4.25 , 3.23 , 50 , 26 , 39 , 23.6 , 4.5 , 3.4 , 36 , 3.4 , 24.7 , 58.3 , 4.25 , 39.89 , 3.4 , 58.3 , 2.04 , 24.7 , 2.04 , 3.6 , 40 , 22.42 , 50 , 24.7 , 54 , 41.8 , 2.04 , 3.6 , 42 , 39.89 , 54 , 24.7 , 58.3 , 38 , 40 ,
30 , 100 , 100 , 25 , 200 , 10 , 10 , 170 , 90 , 130 , 30 , 10 , 10 , 25 , 20 , 20 , 170 , 20 , 15 , 10 , 15 , 15 , 10 , 10 , 50 , 10 , 35 , 150 , 30 , 10 , 20 , 20 , 15 , 50 , 20 , 10 , 20 , 25 , 15 , 130 , 30 , 20 , 10 , 150 , 30 , 110 , 70 , 10 , 35 , 5, 30 , 15 , 40 , 190 , 60 , 10 , 40 , 10 , 25 , 10 , 30 , 20 ,
72 ); 3700 ); 1200 ); 1045 ); 640 ); 440 ); 260 ); 722.5 ); 194.4 ); 442 ); 1140 ); 236 ); 500 ); 950 ); 472 ); 720 ); 722.5 ); 68 ); 750 ); 34 ); 660 ); 390 ); 583 ); 500 ); 225 ); 420 ); 824.6 ); 637.5 ); 96.9 ); 500 ); 520 ); 780 ); 354 ); 225 ); 68 ); 360 ); 68 ); 617.5 ); 874.5 ); 552.5 ); 1196.7 ); 68 ); 583 ); 306 ); 741 ); 224.4 ); 252 ); 400 ); 784.7 ); 250 ); 741 ); 810 ); 1672 ); 387.6 ); 216 ); 420 ); 1595.6 ); 540 ); 617.5 ); 583 ); 1140 ); 800 );
85 INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES(
521 , 551 , 551 , 551 , 551 , 513 , 513 , 513 , 508 , 508 , 515 , 515 , 515 , 542 , 542 , 542 , 542 , 573 , 573 , 573 , 566 , 566 , 566 , 566 , 566 , 520 , 520 , 519 , 519 , 575 , 575 , 547 , 547 , 547 , 547 , 540 , 540 , 567 , 570 , 570 , 571 , 571 , 571 , 571 , 571 , 541 , 541 , 532 , 527 , 527 , 527 , 501 , 564 , 564 , 564 , 537 , 537 , 522 , 522 , 522 , 522 , 554 ,
2, 1, 2, 3, 4, 1, 2, 3, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 4, 5, 1, 2, 1, 2, 1, 2, 1, 2, 3, 4, 1, 2, 1, 1, 2, 1, 2, 3, 4, 5, 1, 2, 1, 1, 2, 3, 1, 1, 2, 3, 1, 2, 1, 2, 3, 4, 1,
102134 , 104350 , 104361 , 102134 , 200376 , 104350 , 104351 , 104362 , 104360 , 104362 , 104350 , 104351 , 102134 , 104350 , 104360 , 104361 , 102134 , 103120 , 103131 , 103141 , 103120 , 103121 , 103130 , 103131 , 103141 , 103130 , 103131 , 103130 , 103140 , 103140 , 200380 , 103130 , 103131 , 103140 , 103141 , 103130 , 103131 , 103140 , 103131 , 103140 , 103131 , 103140 , 102132 , 200376 , 200380 , 103140 , 103141 , 103120 , 103120 , 103121 , 103140 , 103130 , 103130 , 103131 , 102132 , 103130 , 102132 , 103120 , 103121 , 103131 , 103141 , 103131 ,
3.23 , 44 , 50 , 3.4 , 1.92 , 40 , 23.6 , 4.25 , 36 , 4.5 , 40 , 22.42 , 3.4 , 41.8 , 39 , 50 , 3.4 , 22.5 , 4.04 , 20 , 23.75 , 27 , 3.77 , 3.9 , 20 , 4, 3.77 , 3.5 , 19 , 19 , 3.8 , 3.77 , 4.04 , 20 , 20 , 3.98 , 3.9 , 20 , 4.04 , 20 , 4.04 , 19 , 3.4 , 2.16 , 3.8 , 20 , 20 , 21.59 , 21.59 , 27.35 , 19 , 3.6 , 3.77 , 3.9 , 3.23 , 3.6 , 3.23 , 21.59 , 28.8 , 4.04 , 20 , 4.04 ,
30 , 10 , 5, 20 , 200 , 15 , 20 , 100 , 20 , 80 , 20 , 25 , 20 , 25 , 20 , 11 , 10 , 50 , 50 , 15 , 40 , 50 , 60 , 170 , 10 , 10 , 80 , 110 , 30 , 30 , 30 , 80 , 70 , 15 , 5, 40 , 180 , 10 , 90 , 15 , 70 , 25 , 15 , 80 , 30 , 10 , 10 , 60 , 60 , 40 , 35 , 60 , 80 , 120 , 40 , 160 , 30 , 80 , 10 , 90 , 10 , 70 ,
96.9 ); 440 ); 250 ); 68 ); 384 ); 600 ); 472 ); 425 ); 720 ); 360 ); 800 ); 560.5 ); 68 ); 1045 ); 780 ); 550 ); 34 ); 1125 ); 202 ); 300 ); 950 ); 1350 ); 226.2 ); 663 ); 200 ); 40 ); 301.6 ); 385 ); 570 ); 570 ); 114 ); 301.6 ); 282.8 ); 300 ); 100 ); 159.2 ); 702 ); 200 ); 363.6 ); 300 ); 282.8 ); 475 ); 51 ); 172.8 ); 114 ); 200 ); 200 ); 1295.4 ); 1295.4 ); 1094 ); 665 ); 216 ); 301.6 ); 468 ); 129.2 ); 576 ); 96.9 ); 1727.2 ); 288 ); 363.6 ); 200 ); 282.8 );
86 INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES( INSERT INTO item VALUES(
569 , 569 , 569 , 550 , 550 , 550 , 550 , 548 , 548 , 505 , 505 , 577 , 577 , 577 , 533 , 533 , 561 , 561 , 561 , 561 , 506 , 506 , 506 , 530 , 530 , 530 , 530 , 530 , 545 , 557 , 557 , 557 , 557 , 557 , 546 , 546 , 546 , 546 , 546 , 546 , 510 , 510 ,
1, 2, 3, 1, 2, 3, 4, 1, 2, 1, 2, 1, 2, 3, 1, 2, 1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 4, 5, 1, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 2,
103120 , 103130 , 103141 , 103120 , 103140 , 103141 , 102132 , 103140 , 102132 , 103130 , 103140 , 103121 , 103131 , 102132 , 103121 , 103130 , 103120 , 103130 , 103141 , 200376 , 103120 , 103130 , 103141 , 103121 , 103130 , 103140 , 103141 , 102132 , 103140 , 103120 , 103121 , 103140 , 200376 , 200380 , 103120 , 103121 , 103130 , 103131 , 103140 , 103141 , 103120 , 103131 ,
22.5 , 3.6 , 20 , 22.5 , 19 , 20 , 3.4 , 19 , 3.23 , 3.5 , 20 , 30 , 3.9 , 3.23 , 27.35 , 3.77 , 22.5 , 3.77 , 20 , 2.16 , 20.88 , 3.5 , 20 , 25.91 , 3.6 , 19 , 20 , 3.4 , 19 , 23.75 , 30 , 19 , 2.16 , 3.4 , 22.5 , 30 , 3.6 , 3.9 , 19 , 20 , 20.88 , 4.19 ,
50 , 180 , 15 , 60 , 45 , 5, 15 , 30 , 30 , 100 , 15 , 20 , 150 , 25 , 30 , 80 , 80 , 70 , 15 , 90 , 80 , 180 , 15 , 50 , 200 , 40 , 10 , 15 , 25 , 40 , 10 , 35 , 80 , 110 , 50 , 20 , 120 , 140 , 40 , 10 , 60 , 20 ,
1125 ); 648 ); 300 ); 1350 ); 855 ); 100 ); 51 ); 570 ); 96.9 ); 350 ); 300 ); 600 ); 585 ); 80.75 ); 820.5 ); 301.6 ); 1800 ); 263.9 ); 300 ); 194.4 ); 1670.4 ); 630 ); 300 ); 1295.5 ); 720 ); 760 ); 200 ); 51 ); 475 ); 950 ); 300 ); 665 ); 172.8 ); 374 ); 1125 ); 600 ); 432 ); 546 ); 760 ); 200 ); 1252.8 ); 83.8 );
CREATE TABLE price( product_id NUMBER(6,0) NOT NULL, list_price NUMBER(8,2) NULL, min_price NUMBER(8,2) NULL, start_date DATE NOT NULL, end_date DATE NULL, CHECK (product_id IS NOT NULL), CHECK (start_date IS NOT NULL), CHECK (list_price IS NULL OR min_price IS NULL OR min_price <= list_price), CHECK (end_date IS NULL OR start_date <= end_date), PRIMARY KEY (product_id, start_date), FOREIGN KEY (product_id) REFERENCES product(product_id) );
INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES(
100871 , 100890 , 100890 , 100860 ,
5, 58 , 54 , 35 ,
3.20 , '01-JAN-1989' , '01-DEC-1989' ); 46.40 , '01-JAN-1989' , '' ); 40.50 , '01-JUN-1988' , '31-DEC-1988' ); 28.00 , '01-JUN-1990' , '' );
87 INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES( INSERT INTO price VALUES(
100860 , 100860 , 100861 , 100861 , 100861 , 100870 , 100870 , 100871 , 101860 , 101863 , 102130 , 200376 , 200380 , 103120 , 103120 , 103120 , 103121 , 103121 , 103121 , 103130 , 103130 , 103131 , 103131 , 103140 , 103141 , 102132 , 104350 , 104350 , 104350 , 104351 , 104351 , 104351 , 104352 , 104352 , 104352 , 104360 , 104360 , 104361 , 104361 , 104362 , 102134 , 105123 , 105123 , 105123 , 105124 , 105124 , 105124 , 105125 , 105125 , 105126 , 105126 , 105127 , 105128 , 102136 ,
CREATE TABLE salary_grade( grade_id NUMBER(3,0) NOT NULL, lower_bound NUMBER(7,2) NULL, upper_bound NUMBER(7,2) NULL, CHECK (grade_id IS NOT NULL),
32 , 30 , 45 , 42 , 39 , 3, 2, 6, 24 , 13 , 3, 2, 4, 23 , 24 , 25 , 28 , 29 , 30 , 4, 4, 4, 5, 20 , 20 , 3, 40 , 42 , 44 , 24 , 25 , 26 , 50 , 54 , 58 , 36 , 39 , 47 , 50 , 5, 3, 36 , 38 , 40 , 10 , 12 , 15 , 2, 3, 5, 6, 6, 10 , 3,
25.60 , '01-JAN-1990' , '31-MAY-1990' ); 24.00 , '01-JAN-1989' , '31-DEC-1989' ); 36.00 , '01-JUN-1990' , '' ); 33.60 , '01-JAN-1990' , '31-MAY-1990' ); 31.20 , '01-JAN-1989' , '31-DEC-1989' ); 2.40 , '01-JAN-1990' , '' ); 1.90 , '01-JAN-1989' , '01-DEC-1989' ); 4.80 , '01-JAN-1990' , '' ); 18.00 , '15-FEB-1989' , '' ); 9.40 , '15-FEB-1989' , '' ); 2.80 , '18-AUG-1989' , '' ); 1.75 , '15-NOV-1990' , '' ); 3.20 , '15-NOV-1990' , '' ); 18.60 , '01-JAN-1989' , '31-DEC-1989' ); 19.20 , '01-JAN-1990' , '31-MAY-1990' ); 20.00 , '01-JUN-1990' , '' ); 22.30 , '01-JAN-1989' , '31-DEC-1989' ); 23.00 , '01-JAN-1990' , '31-MAY-1990' ); 24.00 , '01-JUN-1990' , '' ); 3.50 , '01-JAN-1989' , '31-DEC-1989' ); 3.60 , '01-JAN-1990' , '' ); 3.60 , '01-JAN-1989' , '31-DEC-1989' ); 3.90 , '01-JAN-1990' , '' ); 15.00 , '15-FEB-1989' , '' ); 15.00 , '15-FEB-1989' , '' ); 2.80 , '18-AUG-1989' , '' ); 32.00 , '01-JAN-1989' , '31-DEC-1989' ); 33.60 , '01-JAN-1990' , '31-MAY-1990' ); 35.30 , '01-JUN-1990' , '' ); 18.90 , '01-JAN-1989' , '31-DEC-1989' ); 19.80 , '01-JAN-1990' , '31-MAY-1990' ); 20.80 , '01-JUN-1990' , '' ); 35.00 , '01-JAN-1989' , '31-DEC-1989' ); 37.80 , '01-JAN-1990' , '31-MAY-1990' ); 40.80 , '01-JUN-1990' , '' ); 29.50 , '01-JAN-1989' , '31-DEC-1989' ); 32.00 , '01-JAN-1990' , '' ); 33.00 , '01-JAN-1989' , '31-DEC-1989' ); 35.00 , '01-JAN-1990' , '' ); 4.00 , '15-FEB-1989' , '' ); 2.80 , '16-AUG-1989' , '' ); 29.00 , '01-JAN-1989' , '31-DEC-1989' ); 32.00 , '01-JAN-1990' , '31-MAY-1990' ); 34.00 , '01-JUN-1990' , '' ); 7.60 , '01-JAN-1989' , '31-DEC-1989' ); 9.00 , '01-JAN-1990' , '31-MAY-1990' ); 10.00 , '01-JUN-1990' , '' ); 1.70 , '01-JAN-1989' , '31-DEC-1989' ); 2.60 , '01-JAN-1990' , '' ); 4.30 , '01-JAN-1989' , '31-DEC-1989' ); 5.00 , '01-JAN-1990' , '' ); 5.00 , '15-NOV-1990' , '' ); 8.00 , '15-NOV-1990' , '' ); 2.80 , '03- JUN -1989' , '' );
88 CHECK ((lower_bound IS NULL AND upper_bound IS NOT NULL) OR (lower_bound IS NOT NULL AND upper_bound IS NULL) OR (lower_bound <= upper_bound)), PRIMARY KEY (grade_id) ); INSERT INTO salary_grade VALUES( INSERT INTO salary_grade VALUES( INSERT INTO salary_grade VALUES( INSERT INTO salary_grade VALUES( INSERT INTO salary_grade VALUES(
1, 2, 3, 4, 5,
700 , 1201 , 1401 , 2001 , 3001 ,
1200 ); 1400 ); 2000 ); 3000 ); 9999 );
commit; Приложение 2. Содержание таблиц пользователя DEMO DEMO@ORCL>SELECT * FROM location; LOCATION_ID REGIONAL_GROUP ----------- -------------------168 MOSCOW 169 KIEV 122 NEW YORK 124 DALLAS 123 CHICAGO 167 BOSTON DEMO@ORCL> SELECT * FROM department; DEPARTMENT_ID NAME LOCATION_ID ------------- -------------- ----------55 SALES 56 OPERATIONS 10 ACCOUNTING 122 20 RESEARCH 124 30 SALES 123 40 OPERATIONS 167 12 RESEARCH 122 13 SALES 122 14 OPERATIONS 122 23 SALES 124 24 OPERATIONS 124 34 OPERATIONS 123 43 SALES 167 11 rows selected. DEMO@ORCL>SELECT * FROM job; JOB_ID FUNCTION ---------- -----------------------------667 CLERK 668 STAFF 669 ANALYST 670 SALESPERSON 671 MANAGER 672 PRESIDENT 6 rows selected. DEMO@ORCL>SELECT * FROM employee; EMPLOYEE_ID
FIRST_NAME
JOB_ID
HIRE_DATE
COMMISSION
89 LAST_NAME M MANAGER_ID SALARY DEPARTMENT_ID ----------- --------------- --------------- - ---------- ---------- -------------------- ---------- ------------7839 KING FRANCIS A 672 17-nov-1985 5000 10 7505 DOYLE JEAN K 671 7839 04-apr-1985 2850 0 13 7555 PETERS DANIEL T 670 7505 31-mar-1985 1250 300 13 7557 SHAW KAREN P 670 7505 02-apr-1985 1250 1200 13 7600 PORTER RAYMOND Y 670 7505 15-apr-1985 1250 900 13 7820 ROSS PAUL S 670 7 505 01-jun-1985 1300 800 13 7950 JENSEN ALICE B 667 7505 15-jan-1987 750 0 13 7506 DENNIS LYNN S 671 7839 15-may-1985 2750 0 23 7560 DUNCAN SARAH S 670 7506 31-may-1985 1250 0 23 7564 LANGE GREGORY J 670 7506 01-jun-1985 1250 300 23 7789 WEST LIVIA N 670 7506 04-apr-1985 1500 1000 23 7954 MURRAY JAMES T 667 7506 16-jan-1987 750 0 23 7507 BAKER LESLIE D 671 7839 10-jun-1985 2200 0 14 7609 LEWIS RICHARD M 668 7507 16-apr-1985 1800 0 14 7676 SOMMERS DENISE D 668 7507 19-apr-1985 1850 0 14 7566 JONES TERRY M 671 7839 02-apr-1985 2975 0 20 7788 SCOTT DONALD T 669 7566 09-dec-1986 3000 0 20 7876 ADAMS DIANE G 667 7788 12-jan-1987 1100 0 20 7902 FORD JENNIFER D 669 7566 03-dec-1985 3000 0 20 7369 SMITH JOHN Q 667 7902 17-dec-1984 800 0 20 7569 ALBERTS CHRIS L 671 7839 06-apr-1985 3000 0 12 7799 FISHER MATTHEW G 669 7569 12-dec-1986 3000 0 12 7919 DOUGLAS MICHAEL A 667 7799 04-jan-1987 800 0 12 7916 ROBERTS GRACE M 669 7569 04-jan-1987 2875 0 12 7698 BLAKE MARION S 671 7839 01-may-1985 2850 0 30 7499 ALLEN KEVIN J 670 7698 20-feb-1985 1600 300 30 7521 WARD CYNTHIA D 670 7698 22-feb-1985 1250 500 30 7654 MARTIN KENNETH J 670 7698 28-sep-1985 1250 1400 30 7844 TURNER MARY A 670 7698 08-sep-1985 1500 0 30 7900 JAMES FRED S 667 7698 03-dec-1985 950 0 30 7782 CLARK CAROL F 671 7839 09-jun-1985 2450 0 10 7934 MILLER BARBARA M 667 7782 23-jan-1986 1300 0 10 32 rows selected.
90
DEMO@ORCL>SELECT * FROM customer; CUSTOMER_ID NAME ----- ---------------100 JOCKSPORTS 101 TKB SPORT SHOP 102 VOLLYRITE 103 JUST TENNIS 104 EVERY MOUNTAIN 105 K + T SPORTS 106 SHAPE UP 107 WOMENS SPORTS 108 NORTH WOODS HEALTH 201 STADIUM SPORTS 202 HOOPS 203 REBOUND SPORTS 204 THE POWER FORWARD 205 POINT GUARD 206 THE COLISEUM 207 FAST BREAK 208 AL AND BOB'S SPORTS 211 AT BAT 212 ALL SPORT 213 GOOD SPORT 214 AL'S PRO SHOP 215 BOB'S FAMILY SPORTS 216 THE ALL AMERICAN 217 HIT, THROW, AND RUN 218 THE OUTFIELD 221 WHEELS AND DEALS 222 JUST BIKES 223 VELO SPORTS 224 JOE'S BIKE SHOP 225 BOB'S SWIM, AND RUN 226 CENTURY SHOP 227 THE TOUR 228 FITNESS FIRST 33 rows selected.
ADDRESS
CITY
ST
ZIP_CODE PHONE_NUMBER CREDIT_LIMIT AREA_CODE SALESPERSON_ID --------------------------------------- -----------------------------------------------345 VIEWRIDGE BELMONT CA 96711 415 5986609 7844 5000 490 BOLI RD. REDWOOD CITY CA 94061 415 3681223 7521 10000 9722 HAMILTON BURLINGAME CA 95133 415 6443341 7654 7000 HILLVIEW MALL BURLINGAME CA 97 415 6779312 7521 3000 574 SURRY RD. CUPERTINO CA 93301 408 9962323 7499 10000 3476 EL PASEO SANTA CLARA CA 91003 408 3769966 7844 5000 908 SEQUOIA PALO ALTO CA 94301 415 3649777 7521 6000 VALCO VILLAGE SUNNYVALE CA 93301 408 9674398 7499 10000 98 LONE PINE WAY HIBBING MN 55649 612 5669123 7844 8000 47 IRVING PL. NEW YORK NY 10003 212 5555335 7557 10000 2345 ADAMS AVE. LEICESTER MA 01524 508 5557542 7820 5000 2 E. 14TH ST. NEW YORK NY 10009 212 5555989 7557 10000 1 KNOTS LANDING DALLAS TX 75248 214 5550505 7560 12000 20 THURSTON ST. YONKERS NY 10956 914 5554766 7557 3000 5678 WILBUR PL. SCARSDALE NY 10583 914 5550217 7557 6000 1000 HERBERT LN. CONCORD MA 01742 508 5551298 7820 7000 260 YORKTOWN CT. AUSTIN TX 78731 512 5557631 7560 4000 234 BEACHEM ST. BROOKLINE MA 02146 617 5557385 7820 8000 1000 38TH ST. BROOKLYN NY 11210 718 5551739 7600 6000 400 46TH ST. SUNNYSIDE NY 11104 718 5553771 7600 5000 45 SPRUCE ST. SPRING TX 77388 713 5555172 7564 8000 400 E. 23RD HOUSTON TX 77026 713 5558015 7654 8000 547 PRENTICE RD. CHELSEA MA 02150 617 5553047 7820 5000 333 WOOD COURT GRAPEVINE TX 76051 817 5552352 7564 6000 346 GARDEN BLVD. FLUSHING NY 11355 718 5552131 7820 4000 2 MEMORIAL DRIVE HOUSTON TX 77007 713 5554139 7789 10000 4000 PARKRIDGE BLVD. DALLAS TX 75205 214 5558735 7789 4000 23 WHITE ST. MALDEN MA 02148 617 5554983 7820 5000 4500 FOX COURT GRAND PRARI E TX 75051 214 5559834 7789 6000 300 HORSECREEK IRVING TX 75039 214 5558388 7789 7000 8 DAGMAR DR. HUNTINGTON NY 11743 516 5553006 7555 4000 2500 GARDNER RD. SOMERVILLE MA 02144 617 5556673 7820 5000 5000 85TH ST. JACKSON HEIGHTS NY 11372 718 5558710 7555 4000
91 DEMO@ORCL>SELECT * FROM sales_order; ORDER_ID CUSTOMER_ID TOTAL ORDER_DATE SHIP_DATE ---------- --------------------- -------------------610 07-jan-1991 101 08-jan-1991 101,4 611 11-jan-1991 102 11-jan-1991 45 612 15-jan-1991 104 20-jan-1991 5860 601 16-nov-1990 106 30-nov-1990 60,8 602 05-jun-1990 102 20-jun-1990 56 600 01-may-1990 103 29-may-1990 42 604 15-jun-1990 106 30-jun-1990 642 605 14-jul-1990 106 30-jul-1990 8374 606 14-jul-1990 100 30-jul-1990 3,4 609 01-aug-1990 100 15-aug-1990 102,5 607 18-jul-1990 104 18-jul-1990 5,6 608 25-jul-1990 104 25-jul-1990 35,2 603 05-jun-1990 102 05-jun-1990 224 620 12-mar-1991 100 12-mar-1991 4450 613 01-feb-1991 108 01-feb-1991 6400 614 01-feb-1991 102 05-feb-1991 23940 616 03-feb-1991 103 10-feb-1991 764 619 22-feb-1991 104 04-mar-1991 1260 617 05-feb-1991 105 03-mar-1991 46370 615 01-feb-1991 107 06-feb-1991 710 618 15-feb-1991 102 06-mar-1991 3083 621 15-mar-1991 100 01-apr-1991 730 509 04-jun-1989 226 10-jun-1989 1174 523 13-feb-1990 226 13-feb-1990 1165 549 27-dec-1990 226 14-jan-1991 1620 507 26-may-1989 228 27-may-1989 886 516 30-sep-1989 228 03-oct-1989 1815 553 02-feb-1991 228 13-feb-1991 4400 526 04-mar-1990 221 07-mar-1990 7700 543 04-sep-1990 221 07-sep-1990 8400 555 04-mar-1991 221 07-mar-1991 8540 559 16-nov-1990 222 23-apr-1991 387,2 528 24-mar-1990 224 24-mar-1990 3770 531 31-mar-1990 224 02-apr-1990 1400 558 31-mar-1991 224 02-apr-1991 1700 525 28-feb-1990 225 03-mar-1990 377 552 02-feb-1991 223 07-feb-1991 555,8 556 10-mar-1991 223 12-mar-1991 85 560 25-apr-1991 223 25-apr-1991 72 565 01-jun-1991 227 03-jun-1991 4900 574 10-oct-1991 201 12-oct-1991 1685 576 13-oct-1991 201 18-oct-1991 2058,9 503 25-mar-1989 201 06-apr-1989 1876 518 20-oct-1989 201 05-nov-1989 2932,5 517 23-oct-1989 201 07-nov-1989 784 544 11-sep-1990 202 15-sep-1990 2358 524 22-feb-1990 202 04-mar-1990 1979 502 10-feb-1989 202 23-feb-1989 500 539 26-jun-1990 202 03-jul-1990 1300 511 17-aug-1989 202 21-aug-1989 647 512 28-aug-1989 203 13-sep-1989 428 562 04-may-1991 203 19-may-1991 2044,5 529 02-apr-1990 203 05-apr-1990 1264,7 563 25-may-1991 204 26-may-1991 889 568 13-jul-1991 204 21-jul-1991 1217,4 504 11-apr-1989 204 12-apr-1989 1434,7
92 538 23-jun-1990 535 28-may-1990 578 19-nov-1991 534 11-may-1990 536 21-may-1990 572 19-aug-1991 514 05-sep-1989 521 20-nov-1989 551 22-jan-1991 513 06-sep-1989 508 26-may-1989 515 19-sep-1989 542 11-aug-1990 573 10-oct-1991 566 09-jun-1991 520 16-nov-1989 519 20-oct-1989 575 04-oct-1991 547 16-oct-1990 540 15-jul-1990 567 05-jul-1991 570 17-jul-1991 571 02-aug-1991 541 08-aug-1990 532 08-apr-1990 527 27-feb-1990 501 06-jan-1989 564 25-may-1991 537 14-jun-1990 522 07-jan-1990 554 18-feb-1991 569 16-jul-1991 550 27-jan-1991 548 26-nov-1990 505 07-apr-1989 577 06-oct-1991 533 28-apr-1990 561 20-apr-1991 506 27-apr-1989 530 03-apr-1990 545 06-oct-1990 557 08-mar-1991 546 17-oct-1990 510 18-jul-1989
204 26-jun-1990 741 204 09-jun-1990 810 204 04-dec-1991 2275,6 206 15-may-1990 420 206 06-jun-1990 2135,6 206 24-aug-1991 1200,5 207 16-sep-1989 1140 207 23-nov-1989 896,9 208 06-feb-1991 1142 208 14-sep-1989 1497 208 08-jun-1989 1080 208 26-sep-1989 1428,5 208 23-aug-1990 2409 201 12-oct-1991 1627 201 12-jun-1991 3389,2 201 19-nov-1989 341,6 201 05-nov-1989 955 201 17-oct-1991 684 202 27-oct-1990 984,4 202 17-jul-1990 861,2 202 07-jul-1991 200 202 24-jul-1991 663,6 202 12-aug-1991 1095,6 203 18-aug-1990 400 203 21-apr-1990 1295,4 204 14-mar-1990 3054,4 204 11-jan-1989 216 204 26-may-1991 898,8 204 26-jun-1990 672,9 204 10-jan-1990 2578,8 204 22-feb-1991 282,8 205 23-jul-1991 2073 205 07-feb-1991 2356 205 12-dec-1990 666,9 206 22-apr-1989 650 206 20-oct-1991 1265,75 206 02-may-1990 1122,1 207 07-may-1991 2558,3 208 10-may-1989 2600,4 208 07-apr-1990 3026,5 208 13-oct-1990 475 208 12-mar-1991 2461,8 208 23-oct-1990 3663 208 21-jul-1989 1336,6
100 rows selected. DEMO@ORCL>SELECT * FROM item; ORDER_ID ITEM_ID PRODUCT_ID ACTUAL_PRICE QUANTITY ---------- ---------- ---------- ------------ ---------- ---------600 1 100861 42 1 42 610 3 100890 58 1 58 611 1 100861 45 1 45 612 1 100860 30 100 3000 601 1 200376 2,4 12 28,8 601 2 100860 32 1 32 602 1 100870 2,8 20 56 604 1 100890 58 3 174 604 2 100861 42 2 84 604 3 100860 32 12 384 603 1 100860 32 7 224 610 1 100860 35 1 35
TOTAL
93 610 613 614 614 612 612 620 620 620 613 613 613 619 617 617 614 616 616 616 616 616 619 619 615 607 615 617 617 617 617 617 617 617 617 609 609 618 618 618 621 621 615 608 608 609 606 605 605 605 605 605 605 612 619 509 509 509 523 523 549 549 549
2 4 1 2 2 3 1 2 3 1 2 3 3 1 2 3 1 2 3 4 5 1 2 1 1 2 3 4 5 6 7 8 9 10 2 3 1 2 3 1 2 3 1 2 1 1 1 2 3 4 5 6 4 4 1 2 3 1 2 1 2 3
100870 200376 100860 100870 100861 101863 100860 200376 102130 100871 101860 200380 102130 100860 100861 100871 100861 100870 100890 102130 200376 200380 200376 100861 100871 100870 100870 100871 100890 101860 101863 102130 200376 200380 100870 100890 100860 100861 100870 100861 100870 100871 101860 100871 100861 102130 100861 100870 100890 101860 101863 102130 100871 100871 105123 105124 102136 102136 105123 105123 105127 105125
2,8 2,2 35 2,8 40,5 10 35 2,4 3,4 5,6 24 4 3,4 35 45 5,6 45 2,8 58 3,4 2,4 4 2,4 45 5,6 2,8 2,8 5,6 58 24 12,5 3,4 2,4 4 2,5 50 35 45 2,8 45 2,8 5 24 5,6 40 3,4 45 2,8 58 24 9,5 3,4 5,5 5,6 35 9 3,4 3,4 37,7 38 6 3
3 8,4 200 440 444 15540 1000 2800 20 810 150 1500 10 350 1000 2400 500 1700 100 560 200 4800 150 600 100 340 50 1750 100 4500 1000 5600 10 450 50 140 2 116 10 34 10 24 100 400 100 240 4 180 1 5,6 100 280 500 1400 500 2800 500 29000 100 2400 200 2500 100 340 200 480 300 1200 5 12,5 1 50 23 805 50 2250 10 28 10 450 100 280 50 250 1 24 2 11,2 1 40 1 3,4 100 4500 500 1400 5 290 50 1200 100 950 10 34 100 550 50 280 30 1050 10 90 10 34 10 34 30 1131 5 190 30 180 200 600
94 549 507 507 516 516 516 553 553 526 526 543 543 555 555 555 559 559 559 559 559 528 531 531 558 558 525 552 552 552 552 556 560 565 565 574 574 576 576 576 576 576 503 503 503 518 518 518 518 518 517 517 544 544 544 544 544 524 524 524 524 502 539
4 1 2 1 2 3 1 2 1 2 1 2 1 2 3 1 2 3 4 5 1 1 2 1 2 1 1 2 3 4 1 1 1 2 1 2 1 2 3 4 5 1 2 3 1 2 3 4 5 1 2 1 2 3 4 5 1 2 3 4 1 1
105124 105123 105124 105124 102136 105125 105127 200376 105123 105124 105123 105124 105123 105124 102136 105123 105124 105127 102136 200376 105123 105124 102136 105124 102136 105123 105123 105124 102136 200376 102136 200376 105123 105124 104350 200380 104350 104351 104362 200376 200380 104350 104351 104352 104350 104351 104360 104362 102134 104352 102134 104350 104351 104352 104361 104362 104350 104351 104362 102134 104352 104351
13 35,3 9 9 3,2 1,9 6 1,75 32 9 34 10 34 10 2,8 40 15 6 3,4 2,4 37,7 11 3 14 3 37,7 40 15 3,4 2,4 3,4 2,4 37 12 41,8 3,2 44 26 4,25 2,16 3,4 38 23,6 50 38 23,6 36 4,25 3,4 50 3,4 44 26 58,3 50 4,5 42 23,56 4,25 3,23 50 26
50 650 20 706 20 180 60 540 250 800 250 475 500 3000 800 1400 100 3200 500 4500 100 3400 500 5000 100 3400 500 5000 50 140 5 200 5 75 5 30 3 10,2 30 72 100 3770 100 1100 100 300 100 1400 100 300 10 377 10 400 10 150 1 3,4 1 2,4 25 85 30 72 100 3700 100 1200 25 1045 200 640 10 440 10 260 170 722,5 90 194,4 130 442 30 1140 10 236 10 500 25 950 20 472 20 720 170 722,5 20 68 15 750 10 34 15 660 15 390 10 583 10 500 50 225 10 420 35 824,6 150 637,5 30 96,9 10 500 20 520
95 539 511 511 511 512 512 562 562 562 529 529 563 563 568 568 568 504 504 504 538 535 578 578 578 534 536 536 572 572 514 521 521 551 551 551 551 513 513 513 508 508 515 515 515 542 542 542 542 573 573 573 566 566 566 566 566 520 520 519 519 575 575
2 1 2 3 1 2 1 2 3 1 2 1 2 1 2 3 1 2 3 1 1 1 2 3 1 1 2 1 2 1 1 2 1 2 3 4 1 2 3 1 2 1 2 3 1 2 3 4 1 2 3 1 2 3 4 5 1 2 1 2 1 2
104360 104351 104362 102134 104360 102134 104351 104352 104362 104350 102134 104352 200376 104351 200376 200380 104350 104351 104352 104351 104352 104350 200376 200380 104350 104350 104352 104351 104352 104350 104350 102134 104350 104361 102134 200376 104350 104351 104362 104360 104362 104350 104351 102134 104350 104360 104361 102134 103120 103131 103141 103120 103121 103130 103131 103141 103130 103131 103130 103140 103140 200380
39 23,6 4,5 3,4 36 3,4 24,7 58,3 4,25 39,89 3,4 58,3 2,04 24,7 2,04 3,6 40 22,42 50 24,7 54 41,8 2,04 3,6 42 39,89 54 24,7 58,3 38 40 3,23 44 50 3,4 1,92 40 23,6 4,25 36 4,5 40 22,42 3,4 41,8 39 50 3,4 22,5 4,04 20 23,75 27 3,77 3,9 20 4 3,77 3,5 19 19 3,8
20 15 50 20 10 20 25 15 130 30 20 10 150 30 110 70 10 35 5 30 15 40 190 60 10 40 10 25 10 30 20 30 10 5 20 200 15 20 100 20 80 20 25 20 25 20 11 10 50 50 15 40 50 60 170 10 10 80 110 30 30 30
780 354 225 68 360 68 617,5 874,5 552,5 1196,7 68 583 306 741 224,4 252 400 784,7 250 741 810 1672 387,6 216 420 1595,6 540 617,5 583 1140 800 96,9 440 250 68 384 600 472 425 720 360 800 560,5 68 1045 780 550 34 1125 202 300 950 1350 226,2 663 200 40 301,6 385 570 570 114
96 547 547 547 547 540 540 567 570 570 571 571 571 571 571 541 541 532 527 527 527 501 564 564 564 537 537 522 522 522 522 554 569 569 569 550 550 550 550 548 548 505 505 577 577 577 533 533 561 561 561 561 506 506 506 530 530 530 530 530 545 557 557
1 2 3 4 1 2 1 1 2 1 2 3 4 5 1 2 1 1 2 3 1 1 2 3 1 2 1 2 3 4 1 1 2 3 1 2 3 4 1 2 1 2 1 2 3 1 2 1 2 3 4 1 2 3 1 2 3 4 5 1 1 2
103130 103131 103140 103141 103130 103131 103140 103131 103140 103131 103140 102132 200376 200380 103140 103141 103120 103120 103121 103140 103130 103130 103131 102132 103130 102132 103120 103121 103131 103141 103131 103120 103130 103141 103120 103140 103141 102132 103140 102132 103130 103140 103121 103131 102132 103121 103130 103120 103130 103141 200376 103120 103130 103141 103121 103130 103140 103141 102132 103140 103120 103121
3,77 4,04 20 20 3,98 3,9 20 4,04 20 4,04 19 3,4 2,16 3,8 20 20 21,59 21,59 27,35 19 3,6 3,77 3,9 3,23 3,6 3,23 21,59 28,8 4,04 20 4,04 22,5 3,6 20 22,5 19 20 3,4 19 3,23 3,5 20 30 3,9 3,23 27,35 3,77 22,5 3,77 20 2,16 20,88 3,5 20 25,91 3,6 19 20 3,4 19 23,75 30
80 70 15 5 40 180 10 90 15 70 25 15 80 30 10 10 60 60 40 35 60 80 120 40 160 30 80 10 90 10 70 50 180 15 60 45 5 15 30 30 100 15 20 150 25 30 80 80 70 15 90 80 180 15 50 200 40 10 15 25 40 10
301,6 282,8 300 100 159,2 702 200 363,6 300 282,8 475 51 172,8 114 200 200 1295,4 1295,4 1094 665 216 301,6 468 129,2 576 96,9 1727,2 288 363,6 200 282,8 1125 648 300 1350 855 100 51 570 96,9 350 300 600 585 80,75 820,5 301,6 1800 263,9 300 194,4 1670,4 630 300 1295,5 720 760 200 51 475 950 300
97 557 557 557 546 546 546 546 546 546 510 510
3 4 5 1 2 3 4 5 6 1 2
103140 200376 200380 103120 103121 103130 103131 103140 103141 103120 103131
19 2,16 3,4 22,5 30 3,6 3,9 19 20 20,88 4,19
35 80 110 50 20 120 140 40 10 60 20
665 172,8 374 1125 600 432 546 760 200 1252,8 83,8
271 rows selected. DEMO@ORCL>SELECT * FROM product; PRODUCT_ID DESCRIPTION ---------- -----------------------------100860 ACE TENNIS RACKET I 100861 ACE TENNIS RACKET II 100870 ACE TENNIS BALLS-3 PACK 100871 ACE TENNIS BALLS-6 PACK 100890 ACE TENNIS NET 101860 SP TENNIS RACKET 101863 SP JUNIOR RACKET 102130 RH: "GUIDE TO TENNIS" 200376 SB ENERGY BAR-6 PACK 200380 SB VITA SNACK-6 PACK 103120 WIFF SOFTBALL BAT I 103121 WIFF SOFTBALL BAT II 103130 WIFF SOFTBALL, SMALL 103131 WIFF SOFTBALL, LARGE 103140 WIFF SOFTBALL MITT (LH) 103141 WIFF SOFTBALL MITT (RH) 102132 RH: "GUIDE TO SOFTBALL" 104350 DUNK BASKETBALL INDOOR 104351 DUNK BASKETBALL OUTDOOR 104352 DUNK BASKETBALL PROFESSIONAL 104360 DUNK HOOP 104361 DUNK HOOP W/FIBERGLASS BOARD 104362 DUNK NETS - RAINBOW 102134 RH: "GUIDE TO BASKETBALL" 105123 YELLOW JERSEY BICYCLE HELMET 105124 YELLOW JERSEY BICYCLE GLOVES 105125 YELLOW JERSEY WATER BOTTLE 105126 YELLOW JERSEY BOTTLE CAGE 105127 YELLOW JERSEY FRAME PUMP 105128 YELLOW JERSEY SADDLE PACK 102136 RH: "GUIDE TO CYCLING" 31 rows selected. DEMO@ORCL> SELECT * FROM price; PRODUCT_ID LIST_PRICE MIN_PRICE START_DATE END_DATE ---------- ---------- ---------- ----------- ----------100871 5 3,2 01-jan-1989 01-dec-1989 100890 58 46,4 01-jan-1989 100890 54 40,5 01-jun-1988 31-dec-1988 100860 35 28 01-jun-1990 100860 32 25,6 01-jan-1990 31-may-1990 100860 30 24 01-jan-1989 31-dec-1989 100861 45 36 01-jun-1990
98 100861 100861 100870 100870 100871 101860 101863 102130 200376 200380 103120 103120 103120 103121 103121 103121 103130 103130 103131 103131 103140 103141 102132 104350 104350 104350 104351 104351 104351 104352 104352 104352 104360 104360 104361 104361 104362 102134 105123 105123 105123 105124 105124 105124 105125 105125 105126 105126 105127 105128 102136
42 39 3 2 6 24 13 3 2 4 23 24 25 28 29 30 4 4 4 5 20 20 3 40 42 44 24 25 26 50 54 58 36 39 47 50 5 3 36 38 40 10 12 15 2 3 5 6 6 10 3
33,6 01-jan-1990 31-may-1990 31,2 01-jan-1989 31-dec-1989 2,4 01-jan-1990 1,9 01-jan-1989 01-dec-1989 4,8 01-jan-1990 18 15-feb-1989 9,4 15-feb-1989 2,8 18-aug-1989 1,75 15-nov-1990 3,2 15-nov-1990 18,6 01-jan-1989 31-dec-1989 19,2 01-jan-1990 31-may-1990 20 01-jun-1990 22,3 01-jan-1989 31-dec-1989 23 01-jan-1990 31-may-1990 24 01-jun-1990 3,5 01-jan-1989 31-dec-1989 3,6 01-jan-1990 3,6 01-jan-1989 31-dec-1989 3,9 01-jan-1990 15 15-feb-1989 15 15-feb-1989 2,8 18-aug-1989 32 01-jan-1989 31-dec-1989 33,6 01-jan-1990 31-may-1990 35,3 01-jun-1990 18,9 01-jan-1989 31-dec-1989 19,8 01-jan-1990 31-may-1990 20,8 01-jun-1990 35 01-jan-1989 31-dec-1989 37,8 01-jan-1990 31-may-1990 40,8 01-jun-1990 29,5 01-jan-1989 31-dec-1989 32 01-jan-1990 33 01-jan-1989 31-dec-1989 35 01-jan-1990 4 15-feb-1989 2,8 16-aug-1989 29 01-jan-1989 31-dec-1989 32 01-jan-1990 31-may-1990 34 01-jun-1990 7,6 01-jan-1989 31-dec-1989 9 01-jan-1990 31-may-1990 10 01-jun-1990 1,7 01-jan-1989 31-dec-1989 2,6 01-jan-1990 4,3 01-jan-1989 31-dec-1989 5 01-jan-1990 5 15-nov-1990 8 15-nov-1990 2,8 03-jun-1989
58 rows selected. DEMO@ORCL>SELECT * FROM salary_grade; GRADE_ID LOWER_BOUND UPPER_BOUND ---------- ----------- ----------1 700 1200 2 1201 1400 3 1401 2000 4 2001 3000 5 3001 9999
99 Приложение 3 Решения отдельных задач SQL олимпиады 2007г. Московский энергетический институт (технический университет) совместно с Oracle СНГ, компанией «ФОРС» и институтом «Информика» Министерства образования и науки РФ в 2007 г. впервые в России организовали олимпиаду по языку SQL версии Oracle. Задания олимпиады уже после нее опубликованы без решений в интернете /5/. Почти все задания сформулированы для предметной области пользователя HR в поставляемой с СУБД Oracle базе данных. В настоящем приложении представлены отдельные, оцениваемые наибольшими баллами сложности, задания и возможные их решения (понятно, что одни и те же SQL запросы могут отрабатываться разным способом). Задание 13 /5/ В задании используется таблица HR.EMPLOYEES. Выбрать сведения о сотрудниках, имеющих оклады, начиная со 2-го ранга по сумме оклада и кончая 5-м. Ранг 1 имеет сотрудник или сотрудники с наибольшим окладом, т. е. находящиеся на первом месте по величине оклада. Выводимые столбцы: 1. Имя (First_name). 2. Фамилия (Last_name). 3. Название отдела. 4. Должность (Job_id). 5. Оклад (Salary). 6. Позиция по окладу (ранг). Строки в выходной таблице должны быть упорядочены по позиции (рангу) оклада и по названию отдела и должности в возрастающем порядке. Дополнительные требования к выполнению Не использовать аналитические функции. Для ранжирования использовать псевдостолбец ROWNUM. Сложность в баллах: 7. Решение. 1. Составим подзапрос, в котором бы присутствовал ранг зарплаты и зарплата SELECT ROWNUM rn1, A.salary FROM (SELECT DISTINCT salary FROM employees ORDER BY salary DESC) A; 2. Результирующий запрос SELECT EMP.first_name, EMP.last_name, DEPT.department_name, EMP.job_id, EMP.salary, D.rn1 s_rank FROM employees EMP, departments DEPT, (SELECT ROWNUM rn1, A.salary FROM (SELECT DISTINCT salary FROM employees ORDER BY salary DESC) A )D WHERE EMP.department_id=DEPT.department_id AND EMP.salary=D.salary AND D.rn1 BETWEEN 2 AND 5 ORDER BY s_rank, DEPT.department_name, EMP.job_id / Задание 14 (на аналитические функции) /5/ В задании используется таблица HR.EMPLOYEES. Вывести из таблицы HR.EMPLOYEES информацию о сотрудниках отделов с номерами 10, 30, 50, 90. Вывод должен быть оформлен в таблицу, содержащую столбцы: 1. Сквозной порядковый номер сотрудника. 2. Порядковый номер сотрудника внутри отдела. 3. Номер отдела для данного сотрудника (Department_id). 4. Должность сотрудника (Job_id). 5. Фамилия сотрудника (Last_name). 6. Оклад (Salary). 7. Ранг зарплаты сотрудника в отделе (1-й самый высокооплачиваемый). Строки в выводимой таблице должны удовлетворять следующим условиям: 1. Строки, представляющие сотрудников одного отдела, должны располагаться друг за другом. 2. Строки, представляющие сотрудников одного отдела, должны
100 располагаться в порядке убывания окладов. Дополнительные требования к выполнению Обратите внимание, что ранг в отделе зависит от нумерации в отделе. 3. Сложность в баллах: 6. Решение. SELECT rownum id, DENSE_RANK() OVER (partition by department_id ORDER BY rn1) id_in_dept, department_id, job_id, last_name, salary, salary_rank FROM (SELECT rownum rn1, department_id, job_id, last_name, salary, salary_rank FROM (SELECT department_id, job_id, last_name,salary, DENSE_RANK() OVER (partition by department_id ORDER BY salary DESC) salary_rank FROM employees WHERE department_id in(10, 30, 50, 90)) a) b ORDER BY department_id, id_in_dept / Задание 17 /5/ Список с дублируемыми сотрудниками Текст задания: имеется таблица с тремя столбами: именем, фамилией и коэффициентом размножения, созданная и загруженная следующим образом: DROP TABLE emp_selected; CREATE table emp_selected ( First_name VARCHAR2(20) NOT NULL , Last_name VARCHAR2(20) NOT NULL , N INTEGER NOT NULL); INSERT INTO emp_selected VALUES('Ellen', 'ABEL', 3); INSERT INTO emp_selected VALUES('Matthew', 'WEISS' ,5); commit; Требуется написать запрос, выводящий на печать таблицу, содержащую строки с именами и фамилиями двух сотрудников. Число строк для каждого сотрудника должно определяться коэффициентом размножения (столбец N исходной таблице). То есть должны быть 3 строки для сотрудника Ellen ABEL и 5 строк для Matthew WEISS. Строки должны быть объединены в группы и отсортированы по фамилии и имени. Кроме того, должны быть пронумерованы элементы внутри группы и присутствовать сквозная нумерация. Этот SELECT должен работать для произвольного количества строк в исходной таблице EMP_SELECTED. В выходной таблице должны присутствовать следующие столбцы: - сквозной номер строки по порядку; - номер сотрудника в группе; - имя (First_name); - фамилия (Last_name). Дополнительные требования к выполнению Пример выходного отчѐта: Сквозной № № в группе Имя Фамилия -------------------------------------------1 1 Ellen ABEL 2 2 Ellen ABEL 3 3 Ellen ABEL 4 1 Matthew WEISS 5 2 Matthew WEISS 6 3 Matthew WEISS 7 4 Matthew WEISS 8 5 Matthew WEISS 8 rows selected. Представленное решение без нумерации также засчитывается. Сложность в баллах: 10.
101 Решение: Первый вариант: SELECT ROWNUM, id_for_the_same_last_name, First_name, Last_name FROM (SELECT DISTINCT LEVEL id_for_the_same_last_name, First_name, Last_name FROM emp_selected CONNECT BY LEVEL<=N ORDER BY last_name, LEVEL) / Приведем пояснение этого варианта. Здесь посмотрим внутренний запрос без distinct: SELECT LEVEL id_for_the_same_last_name, First_name, Last_name FROM emp_selected CONNECT BY LEVEL<=N ORDER BY last_name, LEVEL; ID_FOR_THE_SAME_LAST_NAME FIRST_NAME ------------------------- -------------------- ---------1 Ellen ABEL 2 Ellen ABEL 2 Ellen ABEL 3 Ellen ABEL 3 Ellen ABEL 3 Ellen ABEL 3 Ellen ABEL 1 Matthew WEISS 2 Matthew WEISS 2 Matthew WEISS 3 Matthew WEISS 3 Matthew WEISS 3 Matthew WEISS 3 Matthew WEISS 4 Matthew WEISS 4 Matthew WEISS 4 Matthew WEISS 4 Matthew WEISS 4 Matthew WEISS 4 Matthew WEISS 4 Matthew WEISS 4 Matthew WEISS 5 Matthew WEISS 5 Matthew WEISS 5 Matthew WEISS 5 Matthew WEISS 5 Matthew WEISS 5 Matthew WEISS 5 Matthew WEISS 5 Matthew WEISS
LAST_NAME
30 строк выбрано. Видна такая закономерность: С уровнем один встретилось 20=1 записей с ABEL и WEISS, С уровнем два встретилось 21=2 записей с ABEL и WEISS, С уровнем три встретилось 22=4 записей с ABEL и WEISS, С уровнем 4 встретилось 23=8 записей с WEISS, С уровнем 5 встретилось 23=8 записей с WEISS.
102 Приведем структуру создаваемого запросом дерева, из которого станет понятно, что происходит. LEVEL 1 ABEL WEISS LEVEL 2
ABEL
WEISS
ABEL
WEISS
LEVEL 3
ABEL WEISS ABEL WEISS
ABEL WEISS ABEL WEISS
LEVEL 4
WEISS WEISS WEISS WEISS
WEISS WEISS WEISS WEISS
LEVEL 5
WEISS WEISS WEISS WEISS
WEISS WEISS WEISS WEISS
После третьего уровня «ABEL» уже не может появиться, так как для строки с «ABEL» N=3 и LEVEL<=3. Если в таблицу emp_selected вставить третью строку, например ('Den','TOMPSON', 3), вышеприведенный подзапрос будет строить тернарное дерево на первых трех уровнях. Второй вариант решения (предложен Д.Байковым): SELECT ROWNUM, LEVEL row_in_last_name, First_name, Last_name FROM emp_selected CONNECT BY N=CONNECT_BY_ROOT(N) AND LEVEL<=N / Здесь выстраиваемое дерево является одноарным, т.е. от каждой вершины отходит только одна ветвь. Часть запроса «CONNECT_BY_ROOT(N)» строит отдельные деревья для «ABEL» и «WEISS» с наследованием для «ABEL» только «ABEL», для «WEISS» только «WEISS». Нижеследующий запрос демонстрирует такое построение (сначала отформатируем столбец path): COL path FORMAT A30; SELECT ROWNUM, LEVEL, First_name, Last_name, SYS_CONNECT_BY_PATH(last_name, '/') "Path" FROM emp_selected CONNECT BY N=CONNECT_BY_ROOT(N) AND LEVEL<=N / Однако последний вариант решения дает ошибки при одинаковых значениях N в разных строках таблицы emp_selected. Здесь SQL компилятор начинает строить бинарные деревья (для 2-х записей в таблице emp_selected). Поэтому вариант Д.Байкова необходимо уточнить: SELECT ROWNUM, LEVEL row_in_last_name, First_name, Last_name FROM emp_selected CONNECT BY First_name||Last_name =CONNECT_BY_ROOT(First_name||Last_name) AND LEVEL<=N ORDER BY last_name,level / Задание 18 /5/ Зарплаты новых сотрудников. Текст задания: Из таблицы EMPLOYEES надо выбрать не более 19-ти различных зарплат (столбец SALARY) среди самых новых сотрудников (столбец HIRE_DATE), анализируя не более 50 сотрудников. Желательно осуществить выборку одним SQL-запросом. Сложность в баллах: SQL – 9 PL/SQL – 4. Решение. 1. Ограничим число анализируемых строк (сотрудников) числом 50 SELECT e.salary,e.hire_date FROM (SELECT hire_date, salary FROM employees
103 ORDER BY hire_date DESC) e WHERE rownum<=50 / 2. Теперь в полученном результате выделим первые (по убыванию hire_date) 19 строк с отличающимися зарплатами. Если столбец hire_date выводить не надо, то эти 19 строк с разной зарплатой и есть ответ, в противном случае в первом SELECT-е, помимо h.salary добавится вывод столбца h.hire_date. Эта выводимая дата будет соответствовать дате принятия на работу сотудника, которому первому назначили такую зарплату. По разному выделены подзапросы, работа с которыми позволяет исключить дубликаты строк, дублирующих зарплату (дубликаты, расположенные в подзапросе «f» ниже первого, не выводятся; первый из дубликатов выводится). Такая относительная сложность вызвана неудачей использования опции distinct, нарушающей порядок вывода строк (такой убывающий порядок важно сохранить по столбцу hire_date). SELECT h.salary FROM (SELECT f.salary,f.hire_date FROM (select( rownum) rn1,e.salary,e.hire_date FROM (SELECT hire_date, salary FROM employees order by hire_date desc,salary desc) e WHERE rownum<=50) f WHERE f.rn1 <= all(SELECT g.rn2 FROM (SELECT (rownum) rn2, e.salary,e.hire_date FROM (SELECT hire_date, salary FROM employees order by hire_date desc, salary desc) e WHERE rownum<=50) g WHERE f.salary=g.salary) order by f.hire_date desc, f.salary desc) h WHERE rownum <=19 / Задание 19 /5/ Обмен значениями. Текст задания: Оператор отдела кадров при регистрации сотрудников c номерами 194 и 195 присвоила им данные друг друга. Необходимо исправить эту ошибку. Ситуация осложняется тем, что у вас нет доступа к другим полям таблицы за исключением поля employee_id, являющегося первичным ключом. Необходимо решить задачу одним оператором SQL. Дополнительные требования к выполнению Необходимо решить задачу одним оператором SQL Сложность в баллах: 7. Решение. UPDATE employees SET employee_id=decode(employee_id,205,204,204,205,employee_id)
WHERE employee_id IN (204, 205); !!!После UPDATE, так как, возможно, потребуются исходные данные в других задачах, не забыть выполнить roolback; Задание 22 /5/ Отчет о выплатах подчиненным. Текст задания:
104 Написать запрос, выдающий отчѐт о суммарных выплатах сотрудникам, непосредственно подчиняющихся руководителю (задаѐтся полное имя), по названиям должностей (поле JOBS.Job_Title). Отчѐт должен содержать группы строк. Каждая группа относится к данному руководителю и состоит из регулярных строк, отображающих суммарные выплаты и количество сотрудников на данной должности, непосредственно подчиняющихся этому руководителю. Группу должна завершать строка с итоговыми значениями суммарных выплат и количества сотрудников (для сотрудников, непосредственно подчиняющихся данному руководителю). Кроме того, отчѐт должен быть завершѐн строкой, представляющей общий итог и содержащей количество сотрудников и сумму выплат и по всем упомянутым руководителям. Столбцы отчѐта: 1. Полное имя руководителя Итоговые строки в этом поле должны содержать полное имя руководителя (First_name, пробел,Last_name из таблицы EMPLOYEES) с отступом (несколько точек), за которым следует текстовая константа ―итоги:‖. Для строки, представляющей общий итог, это поле должно содержать текстовую константу ―О Б Щ И Й‖ c отступом, представленным несколькими точками. Для остальных (регулярных) строк в этом поле должно быть полное имя руководителя (First_name, пробел,Last_name) без отступа. 2. Название должности Для итоговых строк этот поле должно содержать название должности руководителя (поле Job_Title из таблицы JOBS) и для строки общего итога – пустое значение, а в поле названия должностей – текстовая константа ―И Т О Г‖. Для регулярных строк здесь должно быть название должности, которую занимают сотрудники с представленными в следующих полях количеством и суммарными выплатами. 3. Количество сотрудников Для строки общего итога – общее количество сотрудников, находящихся в непосредственном подчинении у всех руководителей, представленных в отчѐте. Для итоговых строк – количество сотрудников, находящихся в непосредственном подчинении у данного руководителя. Для регулярных строк – количество сотрудников на данной должности в непосредственном подчинении данного руководителя. 4. Суммарные выплаты Для строки общего итога – сумма выплат сотрудникам, находящимся в непосредственном подчинении у всех руководителей, представленных в отчѐте. Для итоговых строк – суммарные выплаты всем сотрудникам, находящимся в непосредственном подчинении данного руководителя. Для регулярных строк суммарные месячные выплаты для сотрудников, находящихся на данной должности у данного руководителя. Пример отчѐта Руководитель Должность Сотрудников Суммарные выплаты ------------------------------ ------------------------------------- ------------------ ---------------------------Steven King Administration Vice President 2 34000 Steven King Marketing Manager 1 13000 ............................................................ Steven King Stock Manager 5 36400 Steven King итоги: President 14 174050 Neena Kochhar Accounting Manager 1 12000 ............................................................ Neena Kochhar итоги: Administration Vice President 5 44900 Lex De Haan Programmer 1 9000 Lex De Haan итоги: Administration Vice President 1 9000 ............................................................ Den Raphaely Purchasing Clerk 5 13900 Den Raphaely итоги: Purchasing Manager 5 13900 ОБЩИЙ 21 rows selected.
ИТОГ
34 301250
105 Сложность в баллах: 8. Решение (предложено А. Квашниным): col " Полное имя руководителя" format a25; SELECT DECODE(grp_id,0,mgr_name,1,mgr_name||' итоги:',3,' ОБЩИЙ') AS "Полное имя руководителя", DECODE(grp_id,0,emp_job,1,mgr_job,3,' ИТОГ') AS "Название должности", emp_cnt AS "Количество сотрудников", emp_tot_sal AS "Суммарные выплаты" FROM ( SELECT e1.first_name||' '||e1.last_name AS mgr_name, j1.job_title AS mgr_job , j2.job_title AS emp_job , count(*) AS emp_cnt, SUM(e2.salary) AS emp_tot_sal, GROUPING_ID(j1.job_title,j2.job_title) as grp_id FROM employees e1, employees e2, jobs j1, jobs j2 WHERE e1.employee_id=e2.manager_id AND e1.job_id=j1.job_id AND e2.job_id=j2.job_id GROUP BY ROLLUP((e1.first_name||' '||e1.last_name, j1.job_title), j2.job_title) ) ORDER BY mgr_name, grp_id; В завершение этого приложения приведем адрес сайта с задачами и решениями SQL олимпиады 2008 г. http://sql.ru/forum/actualsearch.aspx?search=Oracle+%EE%EB%E8%EC%EF%E8%E0%E4 %E0+2008&sin=0&a=&ma=0&bid=3&dt=-1&s=1&so=1 Автор выражает признательность В.Юринскому, Д.Бойкову, А.Квашнину за отмеченные погрешности и предложенные корректировки в первоначальных вариантах решения отдельных задач.