Advanced SQL
Nataliya Bogushevskaya
2019
CONFIDENTIAL 1
FEW WORDS ABOUT LECTURER
Nataliya Bogushevskaya
• Ukraine, Kyiv
• Software Testing Team Leader
• [email protected]
• Experience:
• 7 years in EPAM
• More than 15 years of production projects (Oracle, Sybase, SQL Server)
CONFIDENTIAL 2
AGENDA
1. PIVOT and UNPIVOT
2. Hierarchical queries with CONNECT BY
CONFIDENTIAL 3
PIVOT AND UNPIVOT
CONFIDENTIAL 4
TASK
Let’s assume, that we have to output the amount of employees by mentioned below jobs:
• SA_REP
• SH_CLERK
• ST_CLERK
• SA_MAN
• ST_MAN
in a pivot view:
CONFIDENTIAL 5
DECISIONS
POSSIBLE DECISION
• With CASE (ANSI)
• With DECODE (Oracle)
CONFIDENTIAL 6
USING PIVOT
WITH t AS (SELECT e.department_id, e.job_id
FROM employees e)
SELECT *
FROM t PIVOT (COUNT(*) FOR job_id IN ('SA_REP',
'SH_CLERK',
'ST_CLERK',
'SA_MAN',
'ST_MAN'));
CONFIDENTIAL 7
TASK
Print out the total salary of employees in each department by mentioned below jobs:
• SA_REP
• SH_CLERK
• ST_CLERK
• SA_MAN
• ST_MAN
in a pivot view:
CONFIDENTIAL 8
SEVERAL AGGREGATE FUNCTIONS
Print out the amount of employees and corresponding total salary for jobs 'SA_REP',
'SH_CLERK', 'ST_CLERK‘ for each department.
WITH t AS (SELECT e.department_id, e.job_id,e.salary
FROM employees e)
SELECT *
FROM t PIVOT (COUNT(*) AS qty,
SUM(salary) AS Sal
FOR job_id IN ('SA_REP', 'SH_CLERK', 'ST_CLERK'));
Note. Column names by default are composed as
<value alias>_<aggregate alias>.
So, only one among several aggregates may have no value.
CONFIDENTIAL 9
SETS OF VALUES IN PIVOT
Find out the amount of employee, hired in 2005 on 'ST_MAN‘ position or in 2007 on
'ST_CLERK‘ position. List:
- department_id
- corresponding amounts in separate columns.
WITH t AS (SELECT e.department_id,
e.job_id,
EXTRACT (YEAR FROM e.hire_date) AS hire
FROM employees e)
SELECT *
FROM t PIVOT (COUNT(*)
FOR (job_id, hire) IN (('ST_MAN',2005) AS stm_2005,
('ST_CLERK',2007) AS stc_2007));
CONFIDENTIAL 10
ANY IN PIVOT
List the amount of employees for each job
WITH t AS (SELECT e.department_id, e.job_id, e. employee_id
FROM employees e)
SELECT *
FROM t PIVOT XML (COUNT (employee_id) FOR job_id IN (ANY));
<PivotSet>
<item>
<column name = "JOB_ID">SH_CLERK</column>
<column name = "COUNT(*)">20</column>
</item>
<item>
<column name = "JOB_ID">ST_CLERK</column>
<column name = "COUNT(*)">20</column>
</PivotSet>
CONFIDENTIAL 11
SUBQUERY IN PIVOT
List the amount of employees on the most highly payed job for each department
WITH t AS (SELECT e.department_id, e.job_id, e. employee_id
FROM employees e)
SELECT *
FROM t PIVOT XML (COUNT (employee_id)
FOR job_id IN (SELECT job_id
FROM jobs
WHERE max_salary =
(SELECT MAX(max_salary)FROM jobs)));
<PivotSet>
<item>
<column name = "JOB_ID">AD_PRES</column>
<column name = COUNT(EMPLOYEE_ID)">0</column>
</item>
</PivotSet>
CONFIDENTIAL 12
USEFUL PIVOT FACTS
• Pivot is executed before any joins or where clauses.
• All columns not mentioned in aggregation clause or in for clause are used as GROUP BY
criteria.
CONFIDENTIAL 13
PRACTICE #1
For each department having employees show the number of people working as
'SA_REP','ST_CLERK','IT_PROG' as well as total salary of such people:
CONFIDENTIAL 14
PRACTICE #2
Show the number of hired people for each year and each month. Take into account
current employees only:
CONFIDENTIAL 15
UNPIVOT
• Reverse PIVOT operation. It converts several columns into values of one
column.
• It can’t undo aggregation
Sales value for
some product
CONFIDENTIAL 16
UNPIVOT
SELECT *
FROM unpivot_test UNPIVOT (sales FOR YEAR IN ("2014","2015","2016"));
CONFIDENTIAL 17
UNPIVOT AND NULL VALUES
SELECT *
FROM unpivot_test UNPIVOT (sales FOR YEAR IN ("2014","2015","2016"))
WHERE country_name = 'Belgium'
CONFIDENTIAL 18
UNPIVOT AND NULL VALUES
SELECT *
FROM unpivot_test UNPIVOT INCLUDE NULLS
(sales FOR YEAR IN ("2014","2015","2016"))
WHERE country_name = 'Belgium';
CONFIDENTIAL 19
HIERARCHICAL QUERIES
WITH CONNECT BY
(ORACLE)
CONFIDENTIAL 20
HIERARCHICAL DATA
Hierarchical data is defined as a set of data items that are related to each
other by hierarchical relationships.
Hierarchical relationships exist where one item of data is the parent of another
item.
Examples of the hierarchical data that is commonly stored in databases include
the following:
• an organizational structure
• a file system
• a set of tasks in a project
• a graph of links between Web pages
CONFIDENTIAL 21
HIERARCHICAL DATA
ROOT
CONFIDENTIAL 22
CONNECT BY
Hierarchical query clause
• START WITH specifies the root row(s) of the hierarchy (not mandatory);
• PRIOR operator refers to the parent row;
• CONNECT BY specifies the relationship between parent rows and child rows of the
hierarchy.
CONFIDENTIAL 23
EXAMPLE
List all managers for some employee:
SELECT employee_id, manager_id, first_name, last_name
FROM employees
START WITH employee_id = 108 Starting point
CONNECT BY employee_id = PRIOR manager_id;
CONFIDENTIAL 24
EXAMPLE
List all subordinates for some employee:
SELECT employee_id, manager_id, first_name, last_name
FROM employees
START WITH employee_id = 108 Starting point
CONNECT BY PRIOR employee_id = manager_id;
CONFIDENTIAL 25
PRACTICE
Select all subordinates of Steven King
CONFIDENTIAL 26
EXECUTION
Oracle processes hierarchical queries as follows:
1. joins;
2. start with/connect by;
3. where;
4. group by and having;
5. order by.
CONFIDENTIAL 27
OPERATORS AND PSEUDOCOLUMNS
PRIOR
• PRIOR – retrieves previous filed in SELECT-clause
For each employee get his/her manager name
SELECT employee_id, manager_id, first_name, last_name
, PRIOR first_name AS m_first_name, PRIOR last_name
AS m_last_name
FROM employees
CONNECT BY PRIOR employee_id = manager_id
START WITH manager_id IS NULL;
CONFIDENTIAL 28
OPERATORS AND PSEUDOCOLUMNS
LEVEL
• LEVEL – returns 1 for a root row, 2 for a child of a root, and so on.
For each employee determine her/his level in hierarchy
SELECT employee_id, manager_id, first_name, last_name,LEVEL
FROM employees
CONNECT BY PRIOR employee_id = manager_id
START WITH manager_id IS NULL;
CONFIDENTIAL 29
PRACTICE
Print list of employees in a tree-view
Note. Use LPAD(' ',(LEVEL-1)*3,'-') for the shifting
CONFIDENTIAL 30
PRACTICE
Generate sequential numbers from 1 to 100
CONFIDENTIAL 31
PRACTICE
Generate dates from now (sysdate) till the end of the year.
CONFIDENTIAL 32
PRACTICE
Select all managers of employee with phone_number = '650.505.2876'. Don't
show the actual employee.
CONFIDENTIAL 33
PRACTICE
The table contains the ordered set or numbers, for example (3, 6, 8, 9, 11 , …).
Return three smallest missing numbers (4, 5, 7).
CONFIDENTIAL 34
PRACTICE
Table contains one row and one field. This field stores some arithmetical
expression with numbers, arithmetic signs and parenthesis’s. For example,
2*((5+7)+2*(2+3))*8)+9
Write a query that will return each symbol from the string as a separate row.
CONFIDENTIAL 35
OPERATORS AND PSEUDOCOLUMNS
SYS_CONNECT_BY_PATH(column, delimiter)
• SYS_CONNECT_BY_PATH – returns the path of a column value from root to node;
For each employee of the 4th level list the full path from the root manager
SELECT e.first_name, e.last_name,
sys_connect_by_path(last_name||' '||first_name,'\') AS org
FROM employees e
WHERE LEVEL = 4
CONNECT BY e.manager_id = PRIOR e.employee_id
START WITH e.employee_id = 100;
CONFIDENTIAL 36
OPERATORS AND PSEUDOCOLUMNS
CONNECT_BY_ROOT
• CONNECT_BY_ROOT - references to the hierarchy root
SELECT last_name Employee,
King CONNECT_BY_ROOT last_name head_manager,
PRIOR last_name AS direct_manager,
LEVEL-1 Pathlen,
Kochhar SYS_CONNECT_BY_PATH(last_name, '/') PATH
FROM employees
Higgins WHERE department_id = 110
CONNECT BY PRIOR employee_id = manager_id
ORDER BY Employee,Pathlen
Gietz
CONFIDENTIAL 37
PRACTICE
For each employee of the 4th level list the full path to the root manager
CONFIDENTIAL 38
OPERATORS AND PSEUDOCOLUMNS
CONNECT_BY_ISLEAF
• CONNECT_BY_ISLEAF – returns 1 for nodes having no children (a leaf), 0 for others
Print all employees of the 3rd level who hasn’t subordinates
SELECT last_name AS Employee,
LEVEL, SYS_CONNECT_BY_PATH(last_name, '/') Path
FROM employees
WHERE CONNECT_BY_ISLEAF = 1
AND LEVEL = 3
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id
ORDER BY manager_id;
CONFIDENTIAL 39
PRACTICE
Find the manager with only one subordinate, who, in turn, has no subordinates
CONFIDENTIAL 40
ORDERING DATA
ORDER SIBLINGS BY – preserves hierarchical ordering
SELECT e.first_name, e.last_name,
sys_connect_by_path(last_name,'\') as org
FROM employees e
CONNECT BY e.manager_id = PRIOR e.employee_id
START WITH e.employee_id = 100
ORDER SIBLINGS BY first_name,last_name;
CONFIDENTIAL 41
PRACTICE
For each employee show his/her salary and summary salary of all his/her
managers. Preserve tree structure in ascending order by salary in output.
CONFIDENTIAL 42
LOOPS
Let’s assume, that some data were modified in the current table:
--set Boss for the root row
UPDATE employees
SET manager_id = 145
WHERE employee_id = 100;
SELECT e.first_name, e.last_name,
sys_connect_by_path(last_name,'\') as org
FROM employees e
CONNECT BY e.manager_id = PRIOR e.employee_id
START WITH e.employee_id = 100
ORDER SIBLINGS BY first_name,last_name;
CONFIDENTIAL 43
LOOPS
NOCYCLE parameter is used to avoid an error and to detect which row is cycling
SELECT e.first_name, e.last_name,
sys_connect_by_path(last_name,'\') as org
FROM employees e
CONNECT BY NOCYCLE e.manager_id = PRIOR e.employee_id
START WITH e.employee_id = 100
ORDER SIBLINGS BY first_name,last_name;
CONFIDENTIAL 44
LOOPS
CONNECT_BY_ISCYCLE - pseudocolumn shows which rows contain the cycle
SELECT e.first_name, e.last_name,
sys_connect_by_path(last_name,'\') as org
FROM employees e
WHERE CONNECT_BY_ISCYCLE = 1
CONNECT BY NOCYCLE e.manager_id = PRIOR e.employee_id
START WITH e.employee_id = 100
ORDER SIBLINGS BY first_name,last_name;
CONFIDENTIAL 45
USEFUL LINKS
Oracle documentation -
https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries003.htm
CONFIDENTIAL 46
On-line DB (Oracle)
https://livesql.oracle.com/apex/livesql/file/index.html
CONFIDENTIAL 47
THANK YOU!
CONFIDENTIAL 48