Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лаврентев Освоение СQЛ 2009

.pdf
Скачиваний:
63
Добавлен:
16.08.2013
Размер:
2.47 Mб
Скачать

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;