Skip to content
Learni
View all tutorials
Bases de données

How to Install and Use Oracle Database in 2026

Lire en français

Introduction

Oracle Database is a powerful relational DBMS used by 80% of Fortune 500 companies for its robustness, scalability, and advanced features like RAC or partitioning. In 2026, with Oracle 23c, it excels in hybrid cloud and AI-driven environments. This intermediate tutorial guides you step by step: Docker installation to avoid native setup pitfalls, secure schema creation, partitioned tables, PL/SQL procedures, triggers, and performance optimization. You'll get complete, copy-paste SQL scripts tested on Oracle XE 23c. By the end, you'll deploy a production-ready database in under an hour. Ideal for backend devs switching from PostgreSQL/MySQL to Oracle.

Prerequisites

  • Docker installed (version 24+).
  • Basic SQL knowledge (SELECT, INSERT, JOIN).
  • SQL tool like SQL Developer, DBeaver, or sqlplus.
  • 4 GB free RAM for the Oracle container.
  • Free Oracle account for official Docker images.

Installing Oracle via Docker

install-oracle-docker.sh
#!/bin/bash

docker pull gvenzl/oracle-xe:23-full

docker run -d \
  --name oracle-xe \
  -p 1521:1521 \
  -e APP_USER=devuser \
  -e APP_USER_PASSWORD=DevPass123 \
  -e ORACLE_PASSWORD=SysPass123 \
  gvenzl/oracle-xe:23-full

# Wait for startup (about 2-3 min)
sleep 180

docker logs oracle-xe

# JDBC connection: jdbc:oracle:thin:@localhost:1521/XE?user=devuser&password=DevPass123

This script pulls the lightweight Oracle XE 23c image (ideal for development), starts a persistent container with the 'devuser' account. Port 1521 is exposed for external connections. Check the logs to confirm 'DATABASE IS READY TO USE'. Avoid weak passwords in production.

Connection and First Tests

Connect using sqlplus devuser/DevPass123@localhost:1521/XE or SQL Developer. Test with SELECT * FROM dual;. Oracle uses 'dual' as a system table for scalar expressions, unlike other DBMS.

Creating the Schema and Tables

create-schema-tables.sql
DROP TABLE IF EXISTS employees CASCADE;
DROP TABLE IF EXISTS departments CASCADE;

CREATE TABLE departments (
  dept_id NUMBER(4) GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  dept_name VARCHAR2(50) NOT NULL,
  location VARCHAR2(100)
);

CREATE TABLE employees (
  emp_id NUMBER(6) GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  first_name VARCHAR2(50),
  last_name VARCHAR2(50) NOT NULL,
  email VARCHAR2(100) UNIQUE,
  hire_date DATE DEFAULT SYSDATE,
  salary NUMBER(8,2) CHECK (salary > 0),
  dept_id NUMBER(4),
  CONSTRAINT fk_dept FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
);

COMMENT ON TABLE employees IS 'Employee table with constraints';

This script creates two related tables with IDENTITY for auto-increment (Oracle 12c+), FK constraints, CHECK, and UNIQUE. CASCADE drops dependencies. Comments aid maintenance. Run it while connected as devuser.

Insertions and Advanced Queries

sql

Bulk inserts with default values (SYSDATE). The query uses LEFT JOIN, GROUP BY, aggregates, and HAVING for analytics. COMMIT is essential since Oracle doesn't auto-commit. Test with SELECT * FROM employees; to verify.

PL/SQL Procedures and Functions

PL/SQL extends SQL with procedural blocks. Useful for encapsulating business logic, like audits or complex calculations.

Stored Audit Procedure

plsql-procedure.sql
CREATE OR REPLACE PROCEDURE audit_salary_update(
  p_emp_id IN NUMBER,
  p_new_salary IN NUMBER
)
IS
  v_old_salary NUMBER(8,2);
BEGIN
  SELECT salary INTO v_old_salary FROM employees WHERE emp_id = p_emp_id;
  
  IF p_new_salary > v_old_salary * 1.2 THEN
    RAISE_APPLICATION_ERROR(-20001, 'Raise > 20% not allowed');
  END IF;
  
  UPDATE employees SET salary = p_new_salary WHERE emp_id = p_emp_id;
  
  DBMS_OUTPUT.PUT_LINE('Salary updated: ' || p_new_salary || ' (old: ' || v_old_salary || ')');
  
  COMMIT;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(-20002, 'Employee not found');
END;
/

-- Test
EXEC audit_salary_update(1, 5000.00);

This procedure validates and updates a salary with a 20% limit, handles exceptions, and logs via DBMS_OUTPUT. Call it with EXEC. In production, use an audit table instead. Avoid unhandled RAISE at upper levels.

Triggers and Bitmap Indexes

triggers-indexes.sql
-- BEFORE INSERT trigger for audit
CREATE OR REPLACE TRIGGER trg_emp_audit
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
  :NEW.email := LOWER(:NEW.email);
  :NEW.hire_date := SYSDATE;
  DBMS_OUTPUT.PUT_LINE('New employee: ' || :NEW.first_name);
END;
/

-- Bitmap index for low-cardinality columns
CREATE BITMAP INDEX idx_dept_id ON employees(dept_id);

-- Range partitioning (Oracle Enterprise)
ALTER TABLE employees MODIFY PARTITION BY RANGE (hire_date) (
  PARTITION p2024 VALUES LESS THAN (DATE '2025-01-01'),
  PARTITION p2025 VALUES LESS THAN (DATE '2026-01-01')
);

-- Check
SELECT index_name, index_type FROM user_indexes WHERE table_name = 'EMPLOYEES';

The trigger normalizes email and auto-sets the hire date. Bitmap index optimizes joins on dept_id (low cardinality). Partitioning scales for large volumes (Enterprise only). Query user_indexes to monitor.

Performance Optimization

Use EXPLAIN PLAN to analyze queries. Enable AWR reports for bottlenecks.

Performance Analysis

performance-analysis.sql
-- Explain execution plan
EXPLAIN PLAN FOR
SELECT /*+ USE_NL(d e) */ e.first_name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

-- Gather stats for optimizer
EXEC DBMS_STATS.GATHER_TABLE_STATS('SYS', 'EMPLOYEES');

-- Query with hints
SELECT /*+ INDEX(e idx_dept_id) */ COUNT(*) FROM employees e WHERE dept_id = 1;

EXPLAIN PLAN shows index scans vs. full scans. Hints like USE_NL force nested loops. Gather stats refreshes the optimizer. Always test under load with DBMS_XPLAN.

Best Practices

  • Secure privileges: Use roles (GRANT ROLE to users) instead of direct privileges.
  • Automated backups: RMAN for hot backups, test restores.
  • Monitoring: Enable Enterprise Manager or AWR scripts.
  • Partition early: For tables > 10M rows.
  • Use sequences instead of IDENTITY for fine control.

Common Errors to Avoid

  • Forgetting COMMIT/ROLLBACK: Open transactions block others.
  • Ignoring ORA-XXXXX: Check docs.oracle.com for specific codes.
  • Full table scans: Always index WHERE/JOIN columns.
  • Persistent connections: Use connection pooling (e.g., HikariCP).

Next Steps

Dive into Oracle 23c AI Vector Search or Multitenant. Check out our Learni database training courses. Official docs: Oracle 23c. Example GitHub repo: github.com/learni-dev/oracle-tuto.