Oracle SQL Basics
Learn the fundamentals of Oracle SQL for enterprise database management
1. Introduction to Oracle SQL
What is Oracle SQL?
Oracle SQL is the implementation of SQL used in Oracle Database, a powerful enterprise RDBMS. It includes standard SQL features plus Oracle-specific extensions.
Oracle Database Architecture
Oracle Database consists of:
- Instance - Memory structures and processes
- Database - Physical files storing data
- Schemas - Logical containers for objects
Types of SQL Commands in Oracle:
- DDL (Data Definition Language):
CREATE,ALTER,DROP,TRUNCATE - DML (Data Manipulation Language):
INSERT,UPDATE,DELETE,MERGE - DQL (Data Query Language):
SELECT - DCL (Data Control Language):
GRANT,REVOKE - TCL (Transaction Control Language):
COMMIT,ROLLBACK,SAVEPOINT
2. Setting Up Oracle Environment
Oracle Installation
Download Oracle Database Express Edition (XE) or Enterprise Edition. Install with Oracle Universal Installer (OUI).
Oracle Live SQL
Practice Oracle SQL online using Oracle's free Live SQL platform.
SQL Developer
Use Oracle SQL Developer, a free graphical tool for database development and administration.
3. Oracle Database Objects
Create Tables in Oracle:
CREATE TABLE employees (
emp_id NUMBER PRIMARY KEY,
first_name VARCHAR2(50) NOT NULL,
last_name VARCHAR2(50) NOT NULL,
hire_date DATE DEFAULT SYSDATE,
salary NUMBER(10,2),
dept_id NUMBER(4),
CONSTRAINT fk_dept FOREIGN KEY (dept_id)
REFERENCES departments(dept_id)
);
-- Oracle-specific table organization
CREATE TABLE sales (
sale_id NUMBER,
sale_date DATE,
amount NUMBER(10,2)
) ORGANIZATION INDEX;
Oracle Data Types
NUMBER- Precision numeric (INTEGER, FLOAT are aliases)VARCHAR2(n)- Variable-length string (Oracle-specific)DATE- Date and time valuesTIMESTAMP- Higher precision date/timeCLOB/BLOB- Large text/binary objectsRAW- Binary data
Oracle Constraints
CONSTRAINT- Named constraintsCHECK- Custom validationON DELETE CASCADE- Cascade deletes
4. Basic Oracle SQL Queries
SELECT Statement
-- Select with Oracle pseudocolumns SELECT ROWID, ROWNUM, e.* FROM employees e WHERE ROWNUM <= 10; -- Oracle's DUAL table SELECT SYSDATE, USER FROM DUAL;
WHERE Clause
SELECT * FROM employees
WHERE hire_date > TO_DATE('01-JAN-2020', 'DD-MON-YYYY');
Oracle-Specific Operators
Comparison:
=, >, <, !=, ^=, <>
Logical:
AND, OR, NOT
Oracle-Specific:
PRIOR (hierarchical queries), CONNECT_BY_ROOT
Oracle Pagination
-- Oracle 12c+ syntax
SELECT * FROM employees
OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY;
-- Traditional Oracle syntax
SELECT * FROM (
SELECT a.*, ROWNUM rnum
FROM (SELECT * FROM employees ORDER BY hire_date) a
WHERE ROWNUM <= 20
) WHERE rnum >= 10;
5. Modifying Data in Oracle
INSERT
-- Single record with sequence INSERT INTO employees VALUES (emp_seq.NEXTVAL, 'John', 'Doe', SYSDATE, 50000, 10); -- Insert from another table INSERT INTO managers SELECT * FROM employees WHERE job_id = 'MANAGER';
UPDATE
-- Update with correlated subquery
UPDATE employees e
SET salary = (SELECT AVG(salary) FROM employees
WHERE dept_id = e.dept_id)
WHERE dept_id = 10;
-- Oracle's RETURNING clause
UPDATE employees
SET salary = salary * 1.1
WHERE emp_id = 100
RETURNING salary INTO :new_salary;
DELETE & MERGE
-- Delete with RETURNING DELETE FROM employees WHERE dept_id = 20 RETURNING emp_id INTO :deleted_ids; -- Oracle's MERGE statement MERGE INTO employees_target t USING employees_source s ON (t.emp_id = s.emp_id) WHEN MATCHED THEN UPDATE SET t.salary = s.salary WHEN NOT MATCHED THEN INSERT VALUES (s.emp_id, s.name, s.salary);
6. Advanced Oracle Filtering
Oracle-Specific Functions
-- DECODE (Oracle's IF-THEN-ELSE)
SELECT emp_name,
DECODE(dept_id, 10, 'Accounting',
20, 'Research',
'Other') AS department
FROM employees;
-- CASE expression (ANSI standard)
SELECT emp_name,
CASE WHEN salary > 100000 THEN 'High'
WHEN salary > 50000 THEN 'Medium'
ELSE 'Low' END AS salary_level
FROM employees;
Hierarchical Queries
-- Oracle CONNECT BY for hierarchies SELECT LEVEL, emp_name, manager_id FROM employees START WITH manager_id IS NULL CONNECT BY PRIOR emp_id = manager_id;
Analytic Functions
-- RANK, DENSE_RANK, ROW_NUMBER
SELECT emp_name, salary,
RANK() OVER (ORDER BY salary DESC) AS salary_rank,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM employees;
-- Window functions
SELECT emp_name, dept_id, salary,
AVG(salary) OVER (PARTITION BY dept_id) AS avg_dept_salary,
salary - AVG(salary) OVER (PARTITION BY dept_id) AS diff_from_avg
FROM employees;
7. Oracle Joins
Oracle Join Syntax
-- ANSI JOIN syntax SELECT e.emp_name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id = d.dept_id; -- Oracle's traditional (+) syntax SELECT e.emp_name, d.dept_name FROM employees e, departments d WHERE e.dept_id = d.dept_id(+); -- LEFT JOIN
-- Partitioned outer join SELECT e.emp_name, d.dept_name, d.location FROM employees e PARTITION BY (e.emp_name) RIGHT OUTER JOIN departments d ON e.dept_id = d.dept_id; -- Flashback query (Oracle temporal) SELECT * FROM employees AS OF TIMESTAMP SYSTIMESTAMP - INTERVAL '1' HOUR;
Set Operations
-- Oracle set operations SELECT emp_name FROM current_employees UNION ALL SELECT emp_name FROM former_employees MINUS SELECT emp_name FROM consultants;
8. Oracle Aggregation
Aggregate Functions
-- Oracle statistical functions
SELECT
COUNT(*) AS emp_count,
AVG(salary) AS avg_salary,
MEDIAN(salary) AS median_salary,
STDDEV(salary) AS salary_stddev,
VARIANCE(salary) AS salary_variance,
LISTAGG(emp_name, ', ') WITHIN GROUP (ORDER BY salary) AS employees
FROM employees
GROUP BY dept_id;
Oracle GROUPING SETS
-- Multiple grouping levels
SELECT
dept_id,
job_id,
COUNT(*) AS emp_count,
SUM(salary) AS total_salary
FROM employees
GROUP BY GROUPING SETS (
(dept_id, job_id),
(dept_id),
(job_id),
()
);
-- CUBE and ROLLUP
SELECT dept_id, job_id, COUNT(*)
FROM employees
GROUP BY CUBE(dept_id, job_id);
9. Oracle Subqueries
Oracle WITH Clause
-- Common Table Expression (CTE)
WITH dept_stats AS (
SELECT dept_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY dept_id
)
SELECT e.emp_name, e.salary, d.avg_salary
FROM employees e
JOIN dept_stats d ON e.dept_id = d.dept_id
WHERE e.salary > d.avg_salary;
Inline Views
SELECT d.dept_name, e.emp_count
FROM departments d
JOIN (
SELECT dept_id, COUNT(*) AS emp_count
FROM employees
GROUP BY dept_id
) e ON d.dept_id = e.dept_id;
Oracle Hierarchical Subqueries
-- Hierarchical subquery
SELECT emp_name
FROM employees
WHERE emp_id IN (
SELECT emp_id
FROM employees
START WITH manager_id = 100
CONNECT BY PRIOR emp_id = manager_id
);
Oracle Flashback Subqueries
-- Version query
SELECT versions_starttime, salary
FROM employees
VERSIONS BETWEEN TIMESTAMP
SYSTIMESTAMP - INTERVAL '1' HOUR AND SYSTIMESTAMP
WHERE emp_id = 100;
10. Oracle Views & Indexes
Oracle Views
-- Create a view with check option CREATE OR REPLACE VIEW active_employees AS SELECT * FROM employees WHERE status = 'ACTIVE' WITH CHECK OPTION CONSTRAINT active_emp_chk; -- Materialized view CREATE MATERIALIZED VIEW emp_dept_summary REFRESH COMPLETE ON DEMAND AS SELECT d.dept_name, COUNT(*) emp_count, AVG(e.salary) avg_salary FROM employees e JOIN departments d ON e.dept_id = d.dept_id GROUP BY d.dept_name;
Oracle Indexes
-- Create various index types CREATE INDEX idx_emp_name ON employees(last_name, first_name); CREATE BITMAP INDEX idx_emp_dept ON employees(dept_id); CREATE UNIQUE INDEX idx_emp_email ON employees(email); -- Function-based index CREATE INDEX idx_emp_upper_name ON employees(UPPER(last_name)); -- Oracle-specific indexes: -- * Reverse key indexes -- * Index-organized tables (IOT) -- * Domain indexes
11. Oracle SQL Functions
String Functions
SELECT
INITCAP('hello world') AS initcap,
INSTR('Oracle', 'a') AS position,
SUBSTR('Oracle', 2, 3) AS substring,
LPAD(salary, 10, '*') AS padded_salary,
REGEXP_REPLACE(phone, '(\d{3})(\d{3})(\d{4})', '(\1) \2-\3') AS formatted_phone
FROM employees;
Date Functions
SELECT
SYSDATE AS current_date,
SYSTIMESTAMP AS current_timestamp,
LAST_DAY(hire_date) AS month_end,
ADD_MONTHS(hire_date, 6) AS review_date,
MONTHS_BETWEEN(SYSDATE, hire_date) AS months_employed,
TO_CHAR(hire_date, 'YYYY-MM-DD HH24:MI:SS') AS formatted_date,
TO_DATE('2023-12-31', 'YYYY-MM-DD') AS converted_date
FROM employees;
Numeric Functions
SELECT
ROUND(123.4567, 2) AS rounded,
TRUNC(123.4567, 2) AS truncated,
MOD(10, 3) AS remainder,
POWER(2, 8) AS power,
SQRT(64) AS square_root,
WIDTH_BUCKET(salary, 30000, 100000, 5) AS salary_bucket,
REMAINDER(10, 3) AS alt_remainder
FROM employees;
12. Oracle Transactions
Oracle Transactions
-- Oracle transaction control SET TRANSACTION READ WRITE; -- or READ ONLY for read consistency -- Explicit transaction UPDATE accounts SET balance = balance - 100 WHERE account_id = 123; UPDATE accounts SET balance = balance + 100 WHERE account_id = 456; -- Savepoints SAVEPOINT before_update; UPDATE employees SET salary = salary * 1.1; -- Rollback to savepoint ROLLBACK TO before_update; COMMIT; -- or ROLLBACK;
Oracle Isolation Levels
READ COMMITTED - Default isolation level
SERIALIZABLE - Transaction-level read consistency
READ ONLY - Read-only transaction
Oracle's Multiversioning - Readers don't block writers and vice versa
Flashback Query
-- Query historical data
SELECT * FROM employees
AS OF TIMESTAMP TO_TIMESTAMP('2023-01-01 12:00:00', 'YYYY-MM-DD HH24:MI:SS');
-- Flashback table
FLASHBACK TABLE employees TO TIMESTAMP SYSTIMESTAMP - INTERVAL '15' MINUTE;
13. PL/SQL Basics
PL/SQL Blocks
-- Anonymous PL/SQL block
DECLARE
v_emp_name VARCHAR2(100);
v_salary NUMBER;
BEGIN
SELECT emp_name, salary INTO v_emp_name, v_salary
FROM employees
WHERE emp_id = 100;
DBMS_OUTPUT.PUT_LINE(v_emp_name || ' earns ' || v_salary);
-- Exception handling
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee not found');
END;
Stored Procedures & Functions
-- Create a stored procedure
CREATE OR REPLACE PROCEDURE adjust_salary(
p_emp_id IN NUMBER,
p_percent IN NUMBER
) AS
v_new_salary NUMBER;
BEGIN
UPDATE employees
SET salary = salary * (1 + p_percent/100)
WHERE emp_id = p_emp_id
RETURNING salary INTO v_new_salary;
COMMIT;
DBMS_OUTPUT.PUT_LINE('New salary: ' || v_new_salary);
END;
-- Create a function
CREATE OR REPLACE FUNCTION get_dept_avg(p_dept_id NUMBER)
RETURN NUMBER IS
v_avg_salary NUMBER;
BEGIN
SELECT AVG(salary) INTO v_avg_salary
FROM employees
WHERE dept_id = p_dept_id;
RETURN v_avg_salary;
END;
14. Oracle Best Practices
Performance
- Use bind variables to avoid hard parsing
- Properly index frequently queried columns
- Use EXPLAIN PLAN to analyze query execution
- Consider materialized views for complex aggregations
SQL Writing
- Use ANSI JOIN syntax instead of Oracle's (+) notation
- Prefer CASE over DECODE for readability
- Use WITH clause for complex subqueries
- Leverage analytic functions when appropriate
Security
- Use roles to manage privileges
- Implement Virtual Private Database (VPD) for row-level security
- Use Transparent Data Encryption (TDE) for sensitive data
- Audit critical database operations
Database Design
- Properly size tablespaces and data files
- Consider partitioning for large tables
- Use appropriate storage parameters (PCTFREE, PCTUSED)
- Regularly gather statistics for the optimizer