← Back to Index

Oracle Reference

0. Connection & Basics

0.1 Client Connect

Connecting to an Oracle instance via the sqlplus CLI.

# Connect using Easy Connect syntax
sqlplus hr/password@localhost:1521/orclpdb

# Connect as SYSDBA (requires OS-level authentication)
sqlplus / as sysdba

# Execute a script and exit
sqlplus hr/password@db_alias @script.sql
TERMINAL OUTPUT
SQL*Plus: Release 21.0.0.0.0 - Production on Wed May 22 10:00:00 2024
Connected to: Oracle Database 21c Express Edition

0.2 CLI Meta-commands

Internal sqlplus commands for session configuration and introspection.

DESC employees;              -- Describe table/view structure
SET PAGESIZE 100;           -- Set rows per page
SET LINESIZE 200;           -- Set line width
COLUMN salary FORMAT $99,999;-- Format specific column output
SPOOL report.txt;           -- Write output to a file
SPOOL OFF;                  -- Close the spool file
EXIT;                       -- Quit sqlplus
TERMINAL OUTPUT
(Table description or configuration applied)

0.3 Comments

Annotating SQL code.

-- Single-line comment for a quick note

/* Multi-line comment
   block for complex logic */
SELECT * FROM dual; -- Dual is a special one-row, one-column table
TERMINAL OUTPUT
DUMMY
-----
X

1. Data Types

1.1 Numerics

The NUMBER type handles both integers and floating-point numbers.

SELECT 
    CAST(1234.56 AS NUMBER(6, 2)) AS p_fixed, -- Precision 6, scale 2
    CAST(123456 AS NUMBER(6)) AS p_int,       -- 6-digit integer
    1.23f AS p_float,                         -- BINARY_FLOAT (32-bit)
    1.23d AS p_double                         -- BINARY_DOUBLE (64-bit)
FROM dual;
TERMINAL OUTPUT
   P_FIXED      P_INT    P_FLOAT   P_DOUBLE
---------- ---------- ---------- ----------
   1234.56     123456       1.23       1.23

1.2 Strings & Binaries

Oracle uses VARCHAR2 (max 4000 bytes in SQL, 32k in PL/SQL) for strings.

SELECT 
    CAST('Fixed' AS CHAR(10)) AS c_char,      -- Space-padded
    CAST('Variable' AS VARCHAR2(20)) AS c_v2, -- Standard string
    TO_CLOB('Long Text') AS c_clob,           -- Character Large Object
    HEXTORAW('DEADBEEF') AS c_blob            -- Binary data (RAW/BLOB)
FROM dual;
TERMINAL OUTPUT
C_CHAR     C_V2       C_CLOB     C_BLOB
---------- ---------- ---------- --------
Fixed      Variable   Long Text  DEADBEEF

1.3 Date & Time

The DATE type includes both date and time (to the second).

SELECT 
    SYSDATE,                                  -- Current date and time
    SYSTIMESTAMP,                             -- High precision with TZ
    CURRENT_DATE,                             -- Session-specific date
    INTERVAL '1' YEAR - INTERVAL '2' MONTH,   -- YM Interval
    INTERVAL '10' DAY + INTERVAL '5' MINUTE   -- DS Interval
FROM dual;
TERMINAL OUTPUT
SYSDATE   SYSTIMESTAMP                      CURRENT_DATE
--------- --------------------------------- ---------
22-MAY-24 22-MAY-24 10:00:00.123456 +00:00 22-MAY-24

1.4 Booleans & Enums

Oracle SQL lacks a native BOOLEAN type. Use NUMBER(1) or CHAR(1) with constraints.

CREATE TABLE app_flags (
    is_active NUMBER(1) CHECK (is_active IN (0, 1)),     -- 0=False, 1=True
    status CHAR(1) CHECK (status IN ('A', 'I', 'P')),    -- Enum-like check
    not_null_col VARCHAR2(10) NOT NULL
);
TERMINAL OUTPUT
Table created.

2. Basic Querying (DQL)

2.1 SELECT & Aliases

Retrieving data using aliases and the DUAL table.

SELECT 
    first_name || ' ' || last_name AS "Full Name", -- Double quotes for case
    salary * 1.1 AS increased_salary,
    'Static Value' AS note
FROM employees;
TERMINAL OUTPUT
Full Name            INCREASED_SALARY NOTE
-------------------- ---------------- ------------
Steven King                     26400 Static Value

2.2 WHERE Filtering

Filtering results with standard and Oracle-specific operators.

SELECT * FROM employees
WHERE department_id IN (10, 20)
  AND last_name LIKE 'S%'
  AND salary BETWEEN 5000 AND 10000
  AND commission_pct IS NOT NULL;
TERMINAL OUTPUT
(Filtered employee records)

2.3 ORDER BY

Sorting results with null placement control.

SELECT last_name, salary, commission_pct
FROM employees
ORDER BY salary DESC, commission_pct ASC NULLS LAST;
TERMINAL OUTPUT
(Sorted records)

2.4 LIMIT / OFFSET

Pagination using FETCH (12c+) or legacy ROWNUM.

-- Modern (12c+) syntax
SELECT employee_id FROM employees
ORDER BY employee_id
OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY;

-- Legacy ROWNUM (requires subquery for correct ordering)
SELECT * FROM (
    SELECT e.*, ROWNUM rnum FROM (SELECT * FROM employees ORDER BY id) e
    WHERE ROWNUM <= 15
) WHERE rnum > 10;
TERMINAL OUTPUT
(5 rows)

3. Joins & Set Operations

3.1 Inner & Outer Joins

Standard ANSI joins are preferred over legacy Oracle (+) syntax.

SELECT e.last_name, d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
LEFT OUTER JOIN locations l ON d.location_id = l.location_id;
TERMINAL OUTPUT
(Joined employee and department data)

3.2 Cross & Self Joins

Generating Cartesian products or hierarchical self-references.

-- Self Join
SELECT e.last_name AS emp, m.last_name AS mgr
FROM employees e
JOIN employees m ON e.manager_id = m.employee_id;

-- Cross Join
SELECT * FROM departments CROSS JOIN regions;
TERMINAL OUTPUT
EMP        MGR
---------- ----------
Whalen     Kochhar

3.3 UNION, INTERSECT, MINUS

Oracle uses MINUS instead of EXCEPT.

SELECT email FROM customers
UNION -- Distinct values (UNION ALL for duplicates)
SELECT email FROM leads
MINUS
SELECT email FROM blocked_users;
TERMINAL OUTPUT
(Distinct list of active emails)

3.4 JOIN with Subqueries

Using inline views as join targets.

SELECT d.department_name, avg_sal.val
FROM departments d
JOIN (
    SELECT department_id, AVG(salary) AS val 
    FROM employees GROUP BY department_id
) avg_sal ON d.department_id = avg_sal.department_id;
TERMINAL OUTPUT
(Departments with their average salaries)

4. Aggregation & Grouping

4.1 GROUP BY

Aggregating rows into buckets.

SELECT department_id, job_id, SUM(salary)
FROM employees
GROUP BY department_id, job_id;
TERMINAL OUTPUT
DEPARTMENT_ID JOB_ID     SUM(SALARY)
------------- ---------- -----------
           90 AD_VP            34000

4.2 HAVING

Filtering aggregated results.

SELECT department_id, COUNT(*)
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 10;
TERMINAL OUTPUT
(Departments with more than 10 employees)

4.3 Aggregate Functions

Standard and Oracle-specific aggregation (like LISTAGG for string concat).

SELECT 
    COUNT(DISTINCT department_id) AS dept_count,
    ROUND(AVG(salary), 2) AS avg_sal,
    LISTAGG(last_name, '; ') WITHIN GROUP (ORDER BY last_name) AS names
FROM employees
WHERE department_id = 30;
TERMINAL OUTPUT
DEPT_COUNT    AVG_SAL NAMES
---------- ---------- ----------------------------------------
         1       4150 Baida; Colmenares; Himuro; Khoo; Raphaely

4.4 DISTINCT

Removing duplicate rows from the output.

SELECT DISTINCT job_id FROM employees;
TERMINAL OUTPUT
JOB_ID
----------
AD_PRES
IT_PROG

5. Advanced Querying

5.1 Subqueries

Correlated and non-correlated subqueries.

-- Scalar subquery in SELECT
SELECT last_name, (SELECT MAX(salary) FROM employees) AS top_sal
FROM employees;

-- Correlated subquery in WHERE
SELECT last_name FROM employees e
WHERE salary > (
    SELECT AVG(salary) FROM employees WHERE department_id = e.department_id
);
TERMINAL OUTPUT
(Employees earning above their department average)

5.2 Common Table Expressions (WITH)

Using WITH to simplify complex queries.

WITH dept_costs AS (
    SELECT department_id, SUM(salary) AS total
    FROM employees GROUP BY department_id
)
SELECT department_id FROM dept_costs 
WHERE total > (SELECT AVG(total) FROM dept_costs);
TERMINAL OUTPUT
(High-cost department IDs)

5.3 Recursive CTEs (CONNECT BY vs WITH)

Oracle’s traditional CONNECT BY vs modern recursive WITH.

-- Traditional CONNECT BY
SELECT LEVEL, last_name, manager_id
FROM employees
START WITH manager_id IS NULL
CONNECT BY PRIOR employee_id = manager_id;

-- Modern Recursive WITH (11gR2+)
WITH emp_hierarchy(id, name, mgr_id, lvl) AS (
    SELECT employee_id, last_name, manager_id, 1 FROM employees WHERE manager_id IS NULL
    UNION ALL
    SELECT e.employee_id, e.last_name, e.manager_id, h.lvl + 1
    FROM employees e JOIN emp_hierarchy h ON e.manager_id = h.id
)
SELECT * FROM emp_hierarchy;
TERMINAL OUTPUT
(Tree-structured employee hierarchy)

6. Window Functions

6.1 OVER & PARTITION BY

Computing values across a set of rows.

SELECT 
    last_name, department_id, salary,
    SUM(salary) OVER(PARTITION BY department_id) AS dept_total,
    ROUND(salary / SUM(salary) OVER(PARTITION BY department_id) * 100, 2) AS pct
FROM employees;
TERMINAL OUTPUT
LAST_NAME  DEPT SALARY DEPT_TOTAL    PCT
---------- ---- ------ ---------- ------
King         90  24000      58000  41.38

6.2 Ranking

Assigning ranks based on a window.

SELECT 
    last_name, salary,
    ROW_NUMBER() OVER(ORDER BY salary DESC) AS row_num,
    RANK() OVER(ORDER BY salary DESC) AS rnk,
    DENSE_RANK() OVER(ORDER BY salary DESC) AS drnk
FROM employees;
TERMINAL OUTPUT
LAST_NAME      SALARY    ROW_NUM        RNK       DRNK
---------- ---------- ---------- ---------- ----------
King            24000          1          1          1
Kochhar         17000          2          2          2
De Haan         17000          3          2          2

6.3 Analytics (LEAD / LAG)

Accessing data from adjacent rows.

SELECT 
    last_name, hire_date,
    LAG(last_name) OVER(ORDER BY hire_date) AS prev_hired,
    LEAD(last_name) OVER(ORDER BY hire_date) AS next_hired
FROM employees;
TERMINAL OUTPUT
LAST_NAME  HIRE_DATE PREV_HIRED NEXT_HIRED
---------- --------- ---------- ----------
King       17-JUN-03            Whalen
Whalen     17-SEP-03 King       Mavris

7. Data Modification (DML)

7.1 INSERT

Standard insert and multi-table insert.

-- Single row
INSERT INTO departments (department_id, department_name)
VALUES (300, 'Data Science');

-- Conditional multi-table insert
INSERT ALL
    WHEN salary > 10000 THEN INTO high_earners (id, sal) VALUES (eid, sal)
    ELSE INTO low_earners (id, sal) VALUES (eid, sal)
SELECT employee_id eid, salary sal FROM employees;
TERMINAL OUTPUT
1 row created.
107 rows created.

7.2 UPDATE & DELETE

Standard data modification.

UPDATE employees SET salary = salary * 1.05
WHERE department_id = 90;

DELETE FROM job_history 
WHERE end_date < TO_DATE('01-JAN-2010', 'DD-MON-YYYY');
TERMINAL OUTPUT
3 rows updated.
12 rows deleted.

7.3 Upsert / Merge

Merging source data into a target table.

MERGE INTO target_table t
USING source_table s ON (t.id = s.id)
WHEN MATCHED THEN
    UPDATE SET t.val = s.val, t.updated_at = SYSDATE
WHEN NOT MATCHED THEN
    INSERT (id, val, created_at) VALUES (s.id, s.val, SYSDATE);
TERMINAL OUTPUT
5 rows merged.

8. Schema Definition (DDL)

8.1 CREATE & ALTER Table

Defining table structure and identity columns (12c+).

CREATE TABLE projects (
    project_id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name VARCHAR2(100) NOT NULL,
    budget NUMBER(12, 2) DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

ALTER TABLE projects ADD (status VARCHAR2(20));
ALTER TABLE projects MODIFY (budget NUMBER(15, 2));
TERMINAL OUTPUT
Table created.
Table altered.

8.2 Constraints

Ensuring data integrity at the schema level.

ALTER TABLE projects ADD CONSTRAINT uq_proj_name UNIQUE (name);
ALTER TABLE projects ADD CONSTRAINT ck_budget CHECK (budget >= 0);
-- Foreign Key with Cascade Delete
ALTER TABLE tasks ADD CONSTRAINT fk_task_proj 
    FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE;
TERMINAL OUTPUT
Table altered.

8.3 Indexes & Performance

Optimizing data access.

CREATE INDEX idx_proj_name ON projects(name);
CREATE BITMAP INDEX idx_emp_gender ON employees(gender); -- For low cardinality
CREATE UNIQUE INDEX idx_emp_email ON employees(email);
TERMINAL OUTPUT
Index created.

8.4 Views & Materialized Views

Stored queries and persisted snapshots.

-- Standard View
CREATE VIEW v_high_sal AS 
SELECT * FROM employees WHERE salary > 15000;

-- Materialized View (cached result)
CREATE MATERIALIZED VIEW mv_dept_sal_summary
BUILD IMMEDIATE
REFRESH COMPLETE ON DEMAND
AS SELECT department_id, SUM(salary) total FROM employees GROUP BY department_id;

EXEC DBMS_MVIEW.REFRESH('mv_dept_sal_summary');
TERMINAL OUTPUT
View created.
Materialized view created.

9. Logic & Administration

9.1 Transactions

Oracle uses implicit transaction starts and manual commits.

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
SAVEPOINT sp1; -- Create intermediate rollback point
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- If error: ROLLBACK TO sp1;
COMMIT; -- Finalize all changes
TERMINAL OUTPUT
Commit complete.

9.2 Triggers & PL/SQL Basics

Server-side procedural logic.

-- Row-level trigger to audit changes
CREATE OR REPLACE TRIGGER trg_audit_sal
BEFORE UPDATE OF salary ON employees
FOR EACH ROW
BEGIN
    INSERT INTO sal_audit (emp_id, old_sal, new_sal, change_date)
    VALUES (:OLD.employee_id, :OLD.salary, :NEW.salary, SYSDATE);
END;
/
TERMINAL OUTPUT
Trigger created.

9.3 Explain Plan

Analyzing query execution performance.

EXPLAIN PLAN FOR 
SELECT * FROM employees WHERE last_name = 'King';

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
TERMINAL OUTPUT
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
| Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)|
|   0 | SELECT STATEMENT            |               |     2 |   136 |     2   (0)|
|   1 |  TABLE ACCESS BY INDEX ROWID| EMPLOYEES     |     2 |   136 |     2   (0)|
|*  2 |   INDEX RANGE SCAN          | EMP_NAME_IX   |     2 |       |     1   (0)|

9.4 Roles & Permissions

Security management.

CREATE ROLE report_user;
GRANT SELECT ON hr.employees TO report_user;
GRANT report_user TO jsmith;

REVOKE DELETE ON hr.departments FROM report_user;
TERMINAL OUTPUT
Role created.
Grant succeeded.