Лаврентев Освоение СQЛ 2009
.pdf31
Внешнее объединение не «сработало». Справа надо вставлять название той таблицы, в которой есть информация 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 |
28-FEB-99 (при вызове 28 |
|
время |
FROM dual; |
февраля 1999 года) |
|
|
|
|
last_day |
Последний день |
SELECT last_day(sysdate) |
31-MAR-99 (при вызове |
|
месяца |
FROM dual; |
12 марта 1999 года) |
|
|
|
|
add_months(d,n) |
Добавляет к дате d |
SELECT add_months(sysdate,2) |
18-MAY-99 (при вызове |
|
(или вычитает из |
FROM dual; |
18 марта 1999 года) |
|
нее) n месяцев |
|
|
|
|
|
|
months_between(f, |
Число месяцев |
SELECT |
13 (при вызове в апреле |
s) |
между датой f и |
months_between(sysdate, |
1998 года) |
|
датой s |
'12-MAR-99') |
|
|
|
FROM dual; |
|
next_day(d,day) |
Ближайший |
SELECT |
05-JAN-98 (при вызове 30 |
|
указанный день |
next_day(sysdate,'Monday') |
декабря 1997 года) |
|
недели day после |
FROM dual; |
|
|
даты d |
|
|
Для работы с функциями обработки календарных дат (табл. II.2) надо знать собственно форматы представления дат. Поэтому далее приведем форматы представления дат в
Oracle (табл. II.3).
Таблица II.3. Наиболее распространенные форматы представления дат
Функция |
Возвращаемое значение |
Пример использования |
Результат |
|
|
|
|
Y, YY |
Последние одна, две или три |
SELECT to_char(sysdate,'YYY') |
999 (для всех дней |
или YYY |
цифры календарного года |
FROM dual; |
1999 года) |
|
|
|
|
SYEAR |
Полный номер календарного года; |
SELECT to_char(sysdate, |
TWO THOUSAND |
или |
при использовании SYEAR перед |
'SYEAR') |
EIGHT (для вызова |
YEAR |
датами до н.э. ставится минус |
FROM dual; |
в 2008 г.) |
|
|
|
|
Q |
Квартал года (первый квартал |
SELECT to_char(sysdate,'Q') |
2 (для всех дней |
|
продолжается с января по март) |
FROM dual; |
июня) |
|
|
|
|
MM |
Номер месяца (в формате 01-12, |
SELECT to_char(sysdate,'MM') |
12 (для всех дней |
|
где 12 соответствует декабрю) |
FROM dual; |
декабря) |
|
|
|
|
RM |
Номер месяца римскими цифрами |
SELECT to_char(sysdate,'RM') |
IV (для всех дней |
|
|
FROM dual; |
апреля) |
|
|
|
|
Month |
Месяц в виде строки из девяти |
SELECT to_char(sysdate, 'Month') |
May и шесть |
|
символов |
FROM dual; |
пробелов (для всех |
|
|
|
дней мая) |
|
|
|
|
|
|
|
40 |
|
|
|
|
WW |
Номер недели в году |
SELECT to_char(sysdate,'WW') |
24 (при вызове 13 |
|
|
FROM dual; |
июня 1998 года) |
|
|
|
|
W |
Номер недели в месяце |
SELECT to_char(sysdate,'W') |
1 (при вызове 1 |
|
|
FROM dual; |
октября 1995 года) |
|
|
|
|
DDD |
Порядковый номер дня в году: 1 |
SELECT to_char(sysdate,'DDD') |
363 (при вызове 29 |
|
января — день номер 001, 1 |
FROM dual; |
декабря 1999 года) |
|
февраля — 032 и т.д. |
|
|
|
|
|
|
DD |
День месяца |
SELECT to_char(sysdate,'DD') |
04 (при вызове 4 |
|
|
FROM dual; |
октября) |
|
|
|
|
D |
День недели (в формате 1-7) |
SELECT to_char(sysdate,'D') |
1 (при вызове 14 |
|
|
FROM dual; |
марта 1999 года) |
|
|
|
|
DY |
Сокращенное название дня недели |
SELECT to_char(sysdate,'DY') |
SUN (при вызове 28 |
|
|
FROM dual; |
марта 1999 года) |
|
|
|
|
НН или |
Час по 12-часовой системе |
SELECT to_char(sysdate,'HH') |
02 (в момент времени |
НН12 |
|
FROM dual; |
02:08, т.е. в 2 часа 8 |
|
|
|
минут после |
|
|
|
полуночи) |
|
|
|
|
НН24 |
Час по 24-часовой системе |
SELECT to_char(sysdate,'HH24') |
14 (в 2 часа 8 минут |
|
|
FROM dual; |
пополудни, т.е. в |
|
|
|
14:08) |
|
|
|
|
Ml |
Минуты (0-59) |
SELECT to_char(sysdate,'MI') |
17 (в 16:17, т.е. в 4 |
|
|
FROM dual; |
часа 17 минут |
|
|
|
пополудни) |
|
|
|
|
SS |
Секунды (0-59) |
SELECT to_char(sysdate,'SS') |
22 (в момент времени |
|
|
FROM dual; |
11:03:22) |
|
|
|
|
Следует привести команды, полезные при заполнении таблиц информацией из SQL скрипта, применять которые нужно, если настройки даты на своем компьютере отличаются от формата даты в скриптах. Параметры настройки даты на своем компьютере определяют командой
DEMO@ORCL>SELECT * FROM nls_session_parameters
WHERE parameter IN ('NLS_LANGUAGE', 'NLS_DATE_FORMAT');
PARAMETER |
VALUE |
------------------------------ |
--------------------------------- |
NLS_LANGUAGE |
RUSSIAN |
NLS_DATE_FORMAT |
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 "Задержка оплаты"