Advanced SQL Features
Advanced Oracle SQL features and techniques
Analytical Functions
Comprehensive window functions with detailed explanations and use cases (Access: SELECT privilege required)
-- WINDOW FUNCTIONS - COMPREHENSIVE EXPLANATION
-- ================================================
-- Window functions perform calculations across a set of table rows related to the current row
-- They don't group rows like GROUP BY, but instead provide access to multiple rows from the current row
-- 1. ROW_NUMBER() - Assigns unique sequential numbers to rows within a partition
-- Syntax: ROW_NUMBER() OVER (PARTITION BY column ORDER BY column)
-- Use case: Ranking employees within each department by salary
SELECT
employee_id,
first_name,
last_name,
salary,
department_id,
-- ROW_NUMBER assigns 1,2,3... for each department, ordered by salary DESC
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank
FROM employees
ORDER BY department_id, salary DESC;
-- 2. RANK() vs DENSE_RANK() - Different ranking methods
-- RANK: Leaves gaps in ranking (1,2,2,4,5...)
-- DENSE_RANK: No gaps in ranking (1,2,2,3,4...)
SELECT
employee_id,
first_name,
last_name,
salary,
-- RANK: If two employees have same salary, both get rank 2, next gets rank 4
RANK() OVER (ORDER BY salary DESC) AS overall_rank,
-- DENSE_RANK: If two employees have same salary, both get rank 2, next gets rank 3
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees
ORDER BY salary DESC;
-- 3. LAG() and LEAD() - Access previous and next row values
-- LAG: Gets value from previous row
-- LEAD: Gets value from next row
-- Useful for: Comparing current value with previous/next values
SELECT
employee_id,
first_name,
last_name,
salary,
-- LAG gets the salary of the previous employee (ordered by salary)
LAG(salary, 1) OVER (ORDER BY salary) AS prev_salary,
-- LEAD gets the salary of the next employee (ordered by salary)
LEAD(salary, 1) OVER (ORDER BY salary) AS next_salary,
-- Calculate difference from previous salary
salary - LAG(salary, 1) OVER (ORDER BY salary) AS salary_difference
FROM employees
ORDER BY salary;
-- 4. FIRST_VALUE() and LAST_VALUE() - Get first/last values in window
-- FIRST_VALUE: Gets the first value in the window
-- LAST_VALUE: Gets the last value in the window (requires ROWS clause)
SELECT
employee_id,
first_name,
last_name,
salary,
department_id,
-- Get the highest salary in each department
FIRST_VALUE(salary) OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_max_salary,
-- Get the lowest salary in each department (using ROWS clause for proper window)
LAST_VALUE(salary) OVER (PARTITION BY department_id ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS dept_min_salary
FROM employees
ORDER BY department_id, salary DESC;
-- 5. CUMULATIVE FUNCTIONS - Running totals and moving averages
-- SUM() OVER: Calculates running totals
-- AVG() OVER: Calculates moving averages
SELECT
order_date,
amount,
-- Running total: Sum of all amounts from first row to current row
SUM(amount) OVER (ORDER BY order_date ROWS UNBOUNDED PRECEDING) AS running_total,
-- Moving average: Average of current row and 2 rows before/after
AVG(amount) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING) AS moving_avg_5_rows,
-- Count of rows in current window (5 rows: 2 before + current + 2 after)
COUNT(*) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING) AS window_count
FROM orders
ORDER BY order_date;
-- 6. PERCENTILE FUNCTIONS - Statistical analysis
-- PERCENT_RANK: Relative rank as percentage (0 to 1)
-- CUME_DIST: Cumulative distribution (0 to 1)
-- NTILE: Divides rows into equal-sized buckets
SELECT
employee_id,
first_name,
last_name,
salary,
department_id,
-- PERCENT_RANK: 0.0 for lowest salary, 1.0 for highest salary in department
PERCENT_RANK() OVER (PARTITION BY department_id ORDER BY salary) AS percent_rank,
-- CUME_DIST: Cumulative distribution function
CUME_DIST() OVER (PARTITION BY department_id ORDER BY salary) AS cumulative_dist,
-- NTILE: Divides employees into 4 equal groups (quartiles)
NTILE(4) OVER (PARTITION BY department_id ORDER BY salary) AS quartile
FROM employees
ORDER BY department_id, salary;
-- 7. WINDOW FRAME CLAUSES - Control which rows are included in calculation
-- ROWS BETWEEN: Physical row-based window
-- RANGE BETWEEN: Value-based window
SELECT
employee_id,
salary,
-- ROWS: Include 1 row before and 1 row after current row
AVG(salary) OVER (ORDER BY salary ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS avg_3_rows,
-- RANGE: Include rows with salary within 1000 of current salary
AVG(salary) OVER (ORDER BY salary RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING) AS avg_range_1000,
-- UNBOUNDED: Include all rows from start to current
SUM(salary) OVER (ORDER BY salary ROWS UNBOUNDED PRECEDING) AS cumulative_sum
FROM employees
ORDER BY salary;
Regular Expressions
Regular expression functions for pattern matching and text processing (Access: SELECT privilege required)
-- REGEXP_LIKE for pattern matching
-- Access: Users with SELECT privilege on table
SELECT
email,
phone,
address
FROM employees
WHERE REGEXP_LIKE(email, '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
AND REGEXP_LIKE(phone, '^\d{3}-\d{3}-\d{4}$');
-- REGEXP_SUBSTR for extraction
-- Access: Users with SELECT privilege on table
SELECT
full_name,
REGEXP_SUBSTR(full_name, '^[A-Za-z]+') AS first_name,
REGEXP_SUBSTR(full_name, '[A-Za-z]+$') AS last_name
FROM employees;
-- REGEXP_REPLACE for substitution
-- Access: Users with SELECT privilege on table
SELECT
phone,
REGEXP_REPLACE(phone, '\D', '') AS digits_only,
REGEXP_REPLACE(phone, '(\d{3})(\d{3})(\d{4})', '(\1) \2-\3') AS formatted_phone
FROM employees;
-- REGEXP_INSTR for position finding
-- Access: Users with SELECT privilege on table
SELECT
email,
REGEXP_INSTR(email, '@') AS at_position,
REGEXP_INSTR(email, '\.', 1, 1) AS first_dot_position
FROM employees;
-- Complex pattern matching
-- Access: Users with SELECT privilege on table
SELECT
product_code,
product_name
FROM products
WHERE REGEXP_LIKE(product_code, '^[A-Z]{2}\d{4}$')
AND REGEXP_LIKE(product_name, '^(Laptop|Desktop|Tablet)');
XML and JSON Functions
XML and JSON processing functions (Access: SELECT privilege required)
-- XML functions
-- Access: Users with SELECT privilege on table
SELECT
XMLTYPE('<employee><id>1</id><name>John Doe</name><salary>50000</salary></employee>') AS xml_data,
XMLTYPE('<employee><id>1</id><name>John Doe</name><salary>50000</salary></employee>').EXTRACT('//name/text()') AS name,
XMLTYPE('<employee><id>1</id><name>John Doe</name><salary>50000</salary></employee>').EXTRACT('//salary/text()').getNumberVal() AS salary
FROM dual;
-- JSON functions (Oracle 12c+)
-- Access: Users with SELECT privilege on table
SELECT
JSON_OBJECT('id', 1, 'name', 'John Doe', 'salary', 50000) AS json_object,
JSON_ARRAY('apple', 'banana', 'cherry') AS json_array,
JSON_QUERY('{"id": 1, "name": "John Doe", "salary": 50000}', '$.name') AS name_value,
JSON_VALUE('{"id": 1, "name": "John Doe", "salary": 50000}', '$.salary') AS salary_value
FROM dual;
-- JSON table function
-- Access: Users with SELECT privilege on table
SELECT
j.id,
j.name,
j.salary
FROM JSON_TABLE(
'[{"id": 1, "name": "John Doe", "salary": 50000}, {"id": 2, "name": "Jane Smith", "salary": 55000}]',
'$[*]' COLUMNS (
id NUMBER PATH '$.id',
name VARCHAR2(100) PATH '$.name',
salary NUMBER PATH '$.salary'
)
) j;
-- XML table function
-- Access: Users with SELECT privilege on table
SELECT
x.id,
x.name,
x.salary
FROM XMLTABLE(
'/employees/employee' PASSING XMLTYPE('<employees><employee><id>1</id><name>John Doe</name><salary>50000</salary></employee></employees>')
COLUMNS (
id NUMBER PATH 'id',
name VARCHAR2(100) PATH 'name',
salary NUMBER PATH 'salary'
)
) x;