Aide-mémoire Oracle SQL et PL/SQL
Connexion SQL*Plus / SQLcl
ObjectifCommande
Se connecter (SQL*Plus) sqlplus user/motdepasse@//host:1521/servicename
sqlplus user/motdepasse@TNS_ALIAS
sqlplus / AS SYSDBA            -- local OS auth
sqlplus sys/motdepasse AS SYSDBA
SQLcl (outil moderne Oracle) sql user/motdepasse@//host:1521/servicename
sql user/motdepasse@TNS_ALIAS
Commandes SQL*Plus utiles SET LINESIZE 200
SET PAGESIZE 50
SET SERVEROUTPUT ON           -- afficher DBMS_OUTPUT
DESCRIBE matable;             -- ou DESC matable
SHOW USER;                    -- utilisateur courant
SHOW SGA;                     -- mémoire SGA
EXIT / QUIT
@fichier.sql                  -- exécuter un fichier SQL
SPOOL /tmp/resultat.txt       -- rediriger vers fichier
SPOOL OFF
Paramètres de session ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
ALTER SESSION SET NLS_LANGUAGE = 'FRENCH';
ALTER SESSION SET TIME_ZONE = '+01:00';
DDL - Définition des Données
Créer une table CREATE TABLE employes ( id NUMBER(10) GENERATED ALWAYS AS IDENTITY PRIMARY KEY, matricule VARCHAR2(20) NOT NULL UNIQUE, nom VARCHAR2(100) NOT NULL, prenom VARCHAR2(100), email VARCHAR2(255), departement VARCHAR2(50), salaire NUMBER(10,2) DEFAULT 0, date_embauche DATE DEFAULT SYSDATE, actif NUMBER(1) DEFAULT 1 CHECK (actif IN (0,1)), CONSTRAINT uq_email UNIQUE (email) ) TABLESPACE USERS;
ALTER TABLE ALTER TABLE employes ADD telephone VARCHAR2(20);
ALTER TABLE employes MODIFY salaire NUMBER(12,2);
ALTER TABLE employes RENAME COLUMN prenom TO first_name;
ALTER TABLE employes DROP COLUMN telephone;

-- Contraintes
ALTER TABLE employes ADD CONSTRAINT fk_dept
FOREIGN KEY (dept_id) REFERENCES departements(id)
ON DELETE SET NULL;
ALTER TABLE employes DROP CONSTRAINT fk_dept;
ALTER TABLE employes DISABLE CONSTRAINT fk_dept;
ALTER TABLE employes ENABLE CONSTRAINT fk_dept;
Types de données Oracle Chaînes : CHAR(n) fixe, VARCHAR2(n) variable (max 4000), CLOB (gros textes), NVARCHAR2(n) Unicode
Numériques : NUMBER(p,s) p=précision s=échelle, INTEGER, FLOAT, BINARY_FLOAT, BINARY_DOUBLE
Dates : DATE (date+heure), TIMESTAMP, TIMESTAMP WITH TIME ZONE, INTERVAL
Binaires : BLOB, RAW(n), LONG RAW
Divers : XMLTYPE, JSON (Oracle 21c), BOOLEAN (Oracle 23c)
Séquences CREATE SEQUENCE seq_users
START WITH 1
INCREMENT BY 1
MINVALUE 1
MAXVALUE 9999999999
NOCYCLE
CACHE 20;

-- Utiliser
SELECT seq_users.NEXTVAL FROM DUAL;  -- valeur suivante
SELECT seq_users.CURRVAL FROM DUAL;  -- valeur courante
INSERT INTO users (id, nom) VALUES (seq_users.NEXTVAL, 'Alice');

-- Supprimer
DROP SEQUENCE seq_users;
Synonymes -- Alias pour une table (ou autre schéma)
CREATE SYNONYM emp FOR schema_rh.employes;
CREATE PUBLIC SYNONYM emp FOR schema_rh.employes;  -- accessible à tous
SELECT * FROM emp;  -- utilise la table schema_rh.employes
DROP SYNONYM emp;
Index CREATE INDEX idx_nom ON employes(nom);
CREATE UNIQUE INDEX idx_email ON employes(email);
CREATE INDEX idx_compose ON employes(nom, prenom);
CREATE BITMAP INDEX idx_dept ON employes(departement);  -- faible cardinalité
CREATE INDEX idx_upper ON employes(UPPER(nom));        -- basé sur fonction
DROP INDEX idx_nom;
VUE / MATERIALIZED VIEW -- Vue standard
CREATE OR REPLACE VIEW vw_employes_actifs AS
SELECT id, nom, prenom, email, salaire FROM employes WHERE actif = 1;
DROP VIEW vw_employes_actifs;

-- Vue matérialisée (données stockées physiquement)
CREATE MATERIALIZED VIEW mv_stats
BUILD IMMEDIATE REFRESH COMPLETE ON DEMAND AS
SELECT departement, COUNT(*) nb, AVG(salaire) avg_sal
FROM employes GROUP BY departement;

EXEC DBMS_MVIEW.REFRESH('MV_STATS');
DML - Manipulation des Données
INSERT INSERT INTO employes (matricule, nom, salaire) VALUES ('E001', 'MARTIN', 3500);

-- Insert multiple (Oracle 23c nativement, sinon INSERT ALL)
INSERT ALL
INTO employes (matricule, nom) VALUES ('E002', 'DUPONT')
INTO employes (matricule, nom) VALUES ('E003', 'DURAND')
SELECT 1 FROM DUAL;

-- Depuis une autre table
INSERT INTO archive_employes SELECT * FROM employes WHERE actif = 0;
COMMIT;
UPDATE UPDATE employes SET salaire = salaire * 1.05 WHERE departement = 'IT';

-- Update depuis une autre table (corrélé)
UPDATE employes e
SET salaire = (SELECT avg_sal FROM stats_dept s WHERE s.dept = e.departement)
WHERE EXISTS (SELECT 1 FROM stats_dept s WHERE s.dept = e.departement);
COMMIT;
DELETE DELETE FROM employes WHERE actif = 0;
DELETE FROM employes WHERE date_embauche < DATE '2015-01-01';
TRUNCATE TABLE employes;  -- vider (COMMIT implicite, non annulable !)
COMMIT;
MERGE (Upsert) MERGE INTO employes e USING source_employes s ON (e.matricule = s.matricule) WHEN MATCHED THEN UPDATE SET e.salaire = s.salaire, e.nom = s.nom WHEN NOT MATCHED THEN INSERT (matricule, nom, salaire) VALUES (s.matricule, s.nom, s.salaire);
COMMIT / ROLLBACK / SAVEPOINT COMMIT;                      -- valider
ROLLBACK;                    -- annuler tout
SAVEPOINT sp1;                -- point de sauvegarde
ROLLBACK TO SAVEPOINT sp1;    -- revenir au point
-- Attention : DDL = COMMIT implicite !
SELECT et Spécificités Oracle
DUAL et expressions SELECT SYSDATE FROM DUAL;        -- date/heure courante
SELECT 2 + 2 FROM DUAL;         -- calcul simple
SELECT UPPER('alice') FROM DUAL; -- test de fonction
SELECT seq_users.NEXTVAL FROM DUAL;
SELECT SYS_GUID() FROM DUAL;     -- UUID
ROWNUM et ROWID -- Limiter les lignes (ancienne méthode, avant Oracle 12c)
SELECT * FROM employes WHERE ROWNUM <= 10;

-- Attention : ROWNUM est attribué AVANT le tri !
-- Pour les N premières lignes triées :
SELECT * FROM (
SELECT * FROM employes ORDER BY salaire DESC
) WHERE ROWNUM <= 5;

-- Oracle 12c+ : FETCH FIRST (SQL standard)
SELECT * FROM employes
ORDER BY salaire DESC
FETCH FIRST 10 ROWS ONLY;

-- Pagination Oracle 12c+
SELECT * FROM employes
ORDER BY id
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
NULL en Oracle -- Attention : '' (chaîne vide) = NULL en Oracle !
SELECT NVL(telephone, 'Aucun') FROM employes;          -- NVL
SELECT NVL2(telephone, 'Oui', 'Non') FROM employes;      -- NVL2
SELECT COALESCE(tel, email, 'Aucun') FROM employes;       -- premier non-NULL
SELECT NULLIF(statut, 0) FROM employes;                  // NULL si statut=0
-- NULL est trié EN DERNIER par défaut (ASC), EN PREMIER (DESC)
ORDER BY telephone NULLS LAST;
ORDER BY telephone NULLS FIRST;
DECODE et CASE -- DECODE (propre à Oracle)
SELECT DECODE(statut, 1, 'Actif', 0, 'Inactif', 'Inconnu') FROM employes;

-- CASE (SQL standard)
SELECT nom,
CASE
WHEN salaire >= 5000 THEN 'Senior'
WHEN salaire >= 3000 THEN 'Confirmé'
ELSE 'Junior'
END AS niveau
FROM employes;
Fonctions Spécifiques Oracle
Chaînes SUBSTR(chaine, debut, longueur)  -- commence à 1
INSTR(chaine, 'mot')             -- position
LPAD(code, 6, '0')              -- "000042"
RPAD(code, 10, '-')
TRIM(' texte ')                 -- ' texte ' → 'texte'
REPLACE(chaine, 'a', 'b')
REGEXP_REPLACE(chaine, '[^0-9]', '')
REGEXP_LIKE(email, '^[A-Z0-9._%+-]+@[A-Z0-9.-]+', 'i')
LISTAGG(nom, ', ') WITHIN GROUP (ORDER BY nom)  -- équivalent GROUP_CONCAT
Dates SYSDATE                             -- date+heure serveur
SYSTIMESTAMP                        -- avec microsecondes
TRUNC(SYSDATE)                       -- minuit aujourd'hui
TRUNC(SYSDATE, 'MM')                 -- 1er du mois
ADD_MONTHS(SYSDATE, 3)               -- dans 3 mois
LAST_DAY(SYSDATE)                    -- fin du mois
NEXT_DAY(SYSDATE, 'LUNDI')            -- prochain lundi
MONTHS_BETWEEN(d1, d2)               -- différence en mois
EXTRACT(YEAR FROM SYSDATE)            -- année
TO_DATE('22/02/2026', 'DD/MM/YYYY')    -- chaîne → date
TO_CHAR(SYSDATE, 'DD/MM/YYYY HH24:MI') -- date → chaîne
TO_NUMBER('42')                      -- chaîne → nombre
Fonctions analytiques / fenêtre SELECT nom, departement, salaire, RANK() OVER (PARTITION BY departement ORDER BY salaire DESC) AS rang, ROW_NUMBER() OVER (PARTITION BY departement ORDER BY salaire DESC) AS no_ligne, DENSE_RANK() OVER (PARTITION BY departement ORDER BY salaire DESC) AS rang_dense, PERCENT_RANK()OVER (ORDER BY salaire) AS pct, NTILE(4) OVER (ORDER BY salaire) AS quartile, SUM(salaire) OVER (PARTITION BY departement) AS total_dept, AVG(salaire) OVER (PARTITION BY departement) AS moy_dept, LAG(salaire, 1, 0) OVER (ORDER BY date_embauche) AS salaire_prec, LEAD(salaire, 1, 0) OVER (ORDER BY date_embauche) AS salaire_suiv, FIRST_VALUE(nom) OVER (PARTITION BY departement ORDER BY salaire DESC) AS top_earner FROM employes;
Jointures et Sous-requêtes
Syntaxe ANSI (recommandée) -- INNER JOIN
SELECT e.nom, d.nom AS dept
FROM employes e
JOIN departements d ON e.dept_id = d.id;

-- LEFT OUTER JOIN
SELECT e.nom, d.nom
FROM employes e
LEFT JOIN departements d ON e.dept_id = d.id;

-- FULL OUTER JOIN (Oracle uniquement en SQL standard)
SELECT e.nom, d.nom
FROM employes e
FULL OUTER JOIN departements d ON e.dept_id = d.id;
CTE (WITH) WITH stats AS (
SELECT departement, AVG(salaire) AS moy_sal
FROM employes GROUP BY departement
)
SELECT e.nom, e.salaire, s.moy_sal,
ROUND(e.salaire / s.moy_sal * 100, 1) AS pct_moy
FROM employes e
JOIN stats s ON s.departement = e.departement
ORDER BY pct_moy DESC;
Hiérarchie (CONNECT BY) -- Requête hiérarchique (arbre organisationnel)
SELECT LEVEL, LPAD(' ', 2*(LEVEL-1)) || nom AS hierarchie,
manager_id
FROM employes
START WITH manager_id IS NULL            -- racine
CONNECT BY PRIOR id = manager_id        -- lien parent/enfant
ORDER SIBLINGS BY nom;

-- Fonctions utiles pour hiérarchies
SYS_CONNECT_BY_PATH(nom, '/')            -- chemin
CONNECT_BY_ISLEAF                        -- 1 si feuille
PL/SQL - Blocs et Structures
Bloc anonyme DECLARE v_nom employes.nom%TYPE; v_sal NUMBER(10,2); v_msg VARCHAR2(200); BEGIN SELECT nom, salaire INTO v_nom, v_sal FROM employes WHERE id = 1; IF v_sal > 5000 THEN v_msg := v_nom || ' est senior'; ELSE v_msg := v_nom || ' est junior'; END IF; DBMS_OUTPUT.PUT_LINE(v_msg); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('Employé introuvable'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('Plusieurs résultats'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Erreur : ' || SQLERRM); END; /
Curseurs DECLARE CURSOR c_emp IS SELECT nom, salaire FROM employes WHERE actif = 1 ORDER BY nom; v_rec c_emp%ROWTYPE; BEGIN -- Boucle FOR (recommandée, plus simple) FOR rec IN (SELECT nom, salaire FROM employes WHERE actif = 1) LOOP DBMS_OUTPUT.PUT_LINE(rec.nom || ' : ' || rec.salaire); END LOOP; -- Curseur explicite OPEN c_emp; LOOP FETCH c_emp INTO v_rec; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_rec.nom); END LOOP; CLOSE c_emp; END; /
Procédure stockée CREATE OR REPLACE PROCEDURE augmenter_salaires ( p_departement IN VARCHAR2, p_pourcentage IN NUMBER, p_nb_modif OUT NUMBER ) AS BEGIN UPDATE employes SET salaire = salaire * (1 + p_pourcentage/100) WHERE departement = p_departement; p_nb_modif := SQL%ROWCOUNT; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END augmenter_salaires; / -- Appel DECLARE v_nb NUMBER; BEGIN augmenter_salaires('IT', 10, v_nb); DBMS_OUTPUT.PUT_LINE(v_nb || ' employés augmentés'); END; /
Fonction CREATE OR REPLACE FUNCTION get_salaire_annuel (p_id IN NUMBER) RETURN NUMBER AS v_salaire NUMBER; BEGIN SELECT salaire * 12 INTO v_salaire FROM employes WHERE id = p_id; RETURN v_salaire; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL; END; / -- Utilisation SELECT nom, get_salaire_annuel(id) AS sal_annuel FROM employes;
Package -- Spécification CREATE OR REPLACE PACKAGE pkg_employes AS PROCEDURE creer(p_nom VARCHAR2, p_sal NUMBER); FUNCTION compter RETURN NUMBER; END pkg_employes; / -- Corps CREATE OR REPLACE PACKAGE BODY pkg_employes AS PROCEDURE creer(p_nom VARCHAR2, p_sal NUMBER) AS BEGIN INSERT INTO employes(nom, salaire) VALUES(p_nom, p_sal); COMMIT; END creer; FUNCTION compter RETURN NUMBER AS v_nb NUMBER; BEGIN SELECT COUNT(*) INTO v_nb FROM employes; RETURN v_nb; END compter; END pkg_employes; / -- Appel EXEC pkg_employes.creer('Alice', 3500); SELECT pkg_employes.compter() FROM DUAL;
Trigger CREATE OR REPLACE TRIGGER trg_audit_employes BEFORE INSERT OR UPDATE OR DELETE ON employes FOR EACH ROW BEGIN IF INSERTING THEN INSERT INTO audit_log(action, table_name, rec_id, date_action) VALUES ('INSERT', 'EMPLOYES', :NEW.id, SYSDATE); ELSIF UPDATING THEN INSERT INTO audit_log(action, table_name, rec_id, date_action) VALUES ('UPDATE', 'EMPLOYES', :OLD.id, SYSDATE); ELSIF DELETING THEN INSERT INTO audit_log(action, table_name, rec_id, date_action) VALUES ('DELETE', 'EMPLOYES', :OLD.id, SYSDATE); END IF; END; /
Administration et Requêtes Système
Gestion des utilisateurs / schémas -- Créer un utilisateur (Oracle 12c+ multitenant : C## en CDB)
CREATE USER rh IDENTIFIED BY motdepasse
DEFAULT TABLESPACE USERS
TEMPORARY TABLESPACE TEMP
QUOTA UNLIMITED ON USERS;

GRANT CONNECT, RESOURCE TO rh;
GRANT CREATE SESSION TO rh;
GRANT DBA TO rh;                      -- droits complets (prudence !)

ALTER USER rh ACCOUNT LOCK;
ALTER USER rh ACCOUNT UNLOCK;
ALTER USER rh IDENTIFIED BY nouvmotdepasse;
DROP USER rh CASCADE;                  -- CASCADE pour supprimer les objets
Vues système utiles (DBA_*/ALL_*/USER_*) -- Tables et colonnes
SELECT * FROM USER_TABLES;                -- tables du schéma courant
SELECT * FROM ALL_TABLES;                 -- tables accessibles
SELECT * FROM USER_TAB_COLUMNS WHERE TABLE_NAME = 'EMPLOYES';
SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'EMPLOYES';
SELECT * FROM USER_INDEXES WHERE TABLE_NAME = 'EMPLOYES';

-- Procédures et fonctions
SELECT * FROM USER_OBJECTS WHERE OBJECT_TYPE IN ('PROCEDURE','FUNCTION','PACKAGE');
SELECT NAME, TEXT FROM USER_SOURCE WHERE NAME = 'PKG_EMPLOYES';

-- Sessions et locks
SELECT * FROM V$SESSION WHERE STATUS = 'ACTIVE';
SELECT * FROM V$LOCK;
SELECT * FROM DBA_BLOCKERS;  -- sessions bloquantes

-- Espace disque
SELECT * FROM DBA_DATA_FILES;
SELECT TABLESPACE_NAME, BYTES/1024/1024 AS MB_USED FROM DBA_SEGMENTS;

-- Erreurs de compilation
SELECT * FROM USER_ERRORS WHERE NAME = 'MON_PACKAGE';
Plan d'exécution (EXPLAIN PLAN) EXPLAIN PLAN FOR
SELECT * FROM employes WHERE departement = 'IT';

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());

-- Avec statistiques réelles (après exécution)
SELECT /*+ GATHER_PLAN_STATISTICS */ * FROM employes WHERE ...
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT => 'ALLSTATS LAST'));
Tuer une session -- Trouver les sessions
SELECT SID, SERIAL#, USERNAME, STATUS, SQL_ID
FROM V$SESSION WHERE USERNAME IS NOT NULL;

-- Tuer une session
ALTER SYSTEM KILL SESSION '<SID>,<SERIAL#>' IMMEDIATE;
Export / Import (Data Pump) # Export schéma complet
expdp rh/motdepasse DIRECTORY=DATA_PUMP_DIR DUMPFILE=rh.dmp LOGFILE=rh.log SCHEMAS=RH

# Export table
expdp rh/motdepasse TABLES=EMPLOYES DUMPFILE=employes.dmp

# Import
impdp rh/motdepasse DIRECTORY=DATA_PUMP_DIR DUMPFILE=rh.dmp SCHEMAS=RH

# Voir le répertoire Data Pump
SELECT DIRECTORY_NAME, DIRECTORY_PATH FROM DBA_DIRECTORIES WHERE DIRECTORY_NAME='DATA_PUMP_DIR';