🔝 Retour au Sommaire
Lorsque vous créez une fonction dans PostgreSQL, le système a besoin de savoir comment cette fonction se comporte pour optimiser son exécution. Plus précisément, PostgreSQL doit répondre à cette question :
"Est-ce que cette fonction retourne toujours le même résultat quand on l'appelle avec les mêmes paramètres ?"
La réponse à cette question détermine la catégorie de volatilité de votre fonction, qui influence directement :
- Les performances : PostgreSQL peut-il mettre en cache le résultat ?
- L'optimisation : PostgreSQL peut-il simplifier la requête ?
- La sécurité : La fonction peut-elle être utilisée dans certains contextes ?
PostgreSQL propose trois catégories de volatilité :
- IMMUTABLE - "Immuable" : Toujours le même résultat
- STABLE - "Stable" : Même résultat pendant une transaction
- VOLATILE - "Volatile" : Résultat peut changer à chaque appel
Une fonction IMMUTABLE (immuable) est une fonction qui :
- ✅ Retourne toujours exactement le même résultat pour les mêmes arguments
- ✅ Ne dépend que de ses paramètres d'entrée
- ✅ N'accède jamais à la base de données
- ✅ Ne produit aucun effet de bord (pas de modification de données)
Pensez à une fonction mathématique pure :
f(x) = x * 2
- Si vous appelez
f(5), vous obtenez toujours10 - Peu importe quand, où, ou combien de fois vous l'appelez
- Le résultat ne dépend que de l'entrée
C'est exactement le comportement d'une fonction IMMUTABLE.
CREATE FUNCTION carre(x INTEGER)
RETURNS INTEGER
LANGUAGE SQL
IMMUTABLE -- On déclare explicitement la volatilité
AS $$
SELECT x * x;
$$;
-- Appels :
SELECT carre(5); -- Retourne toujours 25
SELECT carre(5); -- Encore 25
SELECT carre(5); -- Toujours 25 CREATE FUNCTION celsius_vers_kelvin(celsius NUMERIC)
RETURNS NUMERIC
LANGUAGE SQL
IMMUTABLE
AS $$
SELECT celsius + 273.15;
$$;
-- La conversion est toujours la même
SELECT celsius_vers_kelvin(0); -- Toujours 273.15
SELECT celsius_vers_kelvin(100); -- Toujours 373.15 CREATE FUNCTION calculer_prix_ttc(prix_ht NUMERIC)
RETURNS NUMERIC
LANGUAGE SQL
IMMUTABLE
AS $$
SELECT prix_ht * 1.20; -- TVA fixe de 20%
$$;✅ Performance maximale : PostgreSQL met en cache le résultat
-- PostgreSQL peut optimiser ceci :
SELECT carre(5), carre(5), carre(5)
FROM une_grande_table;
-- En calculant carre(5) une seule fois !✅ Utilisable dans les index
-- On peut créer un index sur une fonction IMMUTABLE
CREATE INDEX idx_temperature_kelvin
ON mesures(celsius_vers_kelvin(temperature));
-- PostgreSQL peut utiliser cet index !✅ Optimisation de requête
-- PostgreSQL peut simplifier la requête avant l'exécution
SELECT * FROM produits WHERE prix < calculer_prix_ttc(100);
-- → Transformé en : WHERE prix < 120.00❌ Interdit : Accès à la base de données
-- ❌ FAUX : Cette fonction ne devrait PAS être IMMUTABLE
CREATE FUNCTION obtenir_nom_client(id INTEGER)
RETURNS TEXT
LANGUAGE SQL
IMMUTABLE -- ERREUR ! Accède à une table
AS $$
SELECT nom FROM clients WHERE id = id;
$$;
-- Si les données de la table changent, le résultat change !❌ Interdit : Dépendance à l'état externe
-- ❌ FAUX : NOW() retourne un temps différent à chaque appel
CREATE FUNCTION ajouter_heure(h INTEGER)
RETURNS TIMESTAMP
LANGUAGE SQL
IMMUTABLE -- ERREUR ! Dépend de NOW()
AS $$
SELECT NOW() + (h || ' hours')::INTERVAL;
$$;❌ Interdit : Génération de valeurs aléatoires
-- ❌ FAUX : random() produit des valeurs différentes
CREATE FUNCTION nombre_aleatoire_multiple(x INTEGER)
RETURNS NUMERIC
LANGUAGE SQL
IMMUTABLE -- ERREUR ! Utilise random()
AS $$
SELECT x * random();
$$;- ✅ Fonctions mathématiques pures (addition, multiplication, racine carrée...)
- ✅ Conversions d'unités (km → miles, °C → °F)
- ✅ Calculs algorithmiques déterministes (hash, checksum)
- ✅ Transformations de chaînes pures (upper, lower sur constantes)
Une fonction STABLE (stable) est une fonction qui :
- ✅ Retourne le même résultat pour les mêmes arguments dans une même transaction
- ✅ Peut accéder à la base de données (lecture seule)
- ✅ Ne modifie jamais les données
⚠️ Le résultat peut changer entre différentes transactions
Pensez à regarder l'heure sur une horloge :
- Pendant que vous regardez, l'heure ne change pas
- Mais si vous revenez demain, l'heure sera différente
Une fonction STABLE est comme cette horloge : stable pendant une "photo" (transaction), mais peut changer entre les photos.
CREATE FUNCTION obtenir_taux_tva()
RETURNS NUMERIC
LANGUAGE SQL
STABLE
AS $$
SELECT valeur::NUMERIC
FROM configuration
WHERE cle = 'taux_tva';
$$;
-- Dans une même transaction, retourne toujours la même valeur
BEGIN;
SELECT obtenir_taux_tva(); -- 20.0
SELECT obtenir_taux_tva(); -- 20.0 (même résultat)
COMMIT;
-- Mais peut changer dans une autre transaction
-- (si quelqu'un met à jour la configuration)CREATE FUNCTION nom_complet_client(client_id INTEGER)
RETURNS TEXT
LANGUAGE SQL
STABLE
AS $$
SELECT prenom || ' ' || nom
FROM clients
WHERE id = client_id;
$$;
-- Pendant une transaction, le nom ne change pas
-- Même si appelé plusieurs foisCREATE FUNCTION est_recent(date_creation TIMESTAMP)
RETURNS BOOLEAN
LANGUAGE SQL
STABLE -- STABLE car utilise NOW()
AS $$
SELECT date_creation > NOW() - INTERVAL '24 hours';
$$;
-- NOW() est STABLE : même valeur dans toute la transaction
BEGIN;
SELECT NOW(); -- 2025-01-15 14:30:00
SELECT est_recent('2025-01-15'); -- Utilise le même NOW()
SELECT NOW(); -- Encore 2025-01-15 14:30:00
COMMIT;✅ Optimisation intra-transaction
-- PostgreSQL peut optimiser ceci :
SELECT nom_complet_client(1), nom_complet_client(1), nom_complet_client(1)
FROM commandes;
-- En calculant nom_complet_client(1) une seule fois par ligne !✅ Lecture de données sécurisée
-- Peut lire des tables sans risque d'incohérence
CREATE FUNCTION calculer_total_commande(commande_id INTEGER)
RETURNS NUMERIC
LANGUAGE SQL
STABLE
AS $$
SELECT SUM(quantite * prix_unitaire)
FROM lignes_commande
WHERE id_commande = commande_id;
$$;✅ Utilisable dans certaines optimisations
-- PostgreSQL peut simplifier partiellement
WHERE prix > obtenir_taux_tva() * 100
-- Le taux sera récupéré une fois au début| Aspect | IMMUTABLE | STABLE |
|---|---|---|
| Accès base de données | ❌ Non | ✅ Oui (lecture) |
| Utilise NOW() | ❌ Non | ✅ Oui |
| Résultat entre transactions | Toujours identique | Peut changer |
| Utilisable dans index | ✅ Oui | ❌ Non |
| Cache entre transactions | ✅ Oui | ❌ Non |
- ✅ Fonctions qui lisent des tables (SELECT uniquement)
- ✅ Fonctions utilisant NOW(), CURRENT_DATE, CURRENT_USER
- ✅ Fonctions de recherche/lookup dans des tables de référence
- ✅ Calculs basés sur des données en base (tarifs, configuration)
Une fonction VOLATILE (volatile) est une fonction qui :
⚠️ Peut retourner un résultat différent à chaque appel, même avec les mêmes arguments⚠️ Peut modifier les données (INSERT, UPDATE, DELETE)⚠️ Peut avoir des effets de bord visibles⚠️ Aucune garantie de stabilité
Pensez à lancer un dé :
- Même si vous le lancez toujours de la même façon
- Le résultat change à chaque fois
- Imprévisible et non-déterministe
Une fonction VOLATILE est comme ce dé : on ne sait jamais à l'avance ce qu'elle va retourner.
CREATE FUNCTION generer_code_promo()
RETURNS TEXT
LANGUAGE SQL
VOLATILE -- Par défaut si non spécifié
AS $$
SELECT 'PROMO' || floor(random() * 10000)::TEXT;
$$;
-- Chaque appel produit un résultat différent
SELECT generer_code_promo(); -- 'PROMO7423'
SELECT generer_code_promo(); -- 'PROMO1892'
SELECT generer_code_promo(); -- 'PROMO5631' CREATE FUNCTION incrementer_compteur(compteur_nom TEXT)
RETURNS INTEGER
LANGUAGE plpgsql
VOLATILE -- Modifie des données !
AS $$
DECLARE
nouvelle_valeur INTEGER;
BEGIN
UPDATE compteurs
SET valeur = valeur + 1
WHERE nom = compteur_nom
RETURNING valeur INTO nouvelle_valeur;
RETURN nouvelle_valeur;
END;
$$;
-- Chaque appel modifie la base et retourne une valeur différente
SELECT incrementer_compteur('visiteurs'); -- 1001
SELECT incrementer_compteur('visiteurs'); -- 1002
SELECT incrementer_compteur('visiteurs'); -- 1003 CREATE FUNCTION prochain_numero_facture()
RETURNS INTEGER
LANGUAGE SQL
VOLATILE
AS $$
SELECT nextval('seq_factures');
$$;
-- nextval() change l'état de la séquence
SELECT prochain_numero_facture(); -- 1
SELECT prochain_numero_facture(); -- 2
SELECT prochain_numero_facture(); -- 3 CREATE FUNCTION obtenir_taux_change_actuel(devise TEXT)
RETURNS NUMERIC
LANGUAGE plpgsql
VOLATILE -- Résultat dépend d'un système externe
AS $$
BEGIN
-- Simule un appel API qui change constamment
-- (dans la réalité, utiliserait une extension comme http)
RETURN random() * 0.1 + 1.15; -- Taux fluctuant
END;
$$;❌ Aucune optimisation
-- PostgreSQL NE peut PAS optimiser ceci :
SELECT generer_code_promo(), generer_code_promo()
FROM commandes;
-- Chaque appel sera réellement exécuté (aucun cache)❌ Pas d'utilisation dans les index
-- ❌ IMPOSSIBLE de créer un index
CREATE INDEX idx_code ON commandes(generer_code_promo());
-- Erreur : functions in index expression must be marked IMMUTABLE-- Si vous ne spécifiez rien, la fonction est VOLATILE par défaut
CREATE FUNCTION ma_fonction()
RETURNS INTEGER
LANGUAGE SQL
AS $$
SELECT 1;
$$;
-- Cette fonction est VOLATILE (même si elle pourrait être IMMUTABLE)- ✅ Génération de nombres aléatoires (random(), uuid_generate_v4())
- ✅ Modification de données (INSERT, UPDATE, DELETE)
- ✅ Lecture avec effets de bord (nextval(), lastval())
- ✅ Interaction avec des systèmes externes
- ✅ Fonctions non-déterministes par nature
| Caractéristique | IMMUTABLE | STABLE | VOLATILE |
|---|---|---|---|
| Même résultat avec mêmes arguments | ✅ Toujours | ✅ Dans une transaction | ❌ Non garanti |
| Accès base de données (SELECT) | ❌ Non | ✅ Oui | ✅ Oui |
| Modification de données | ❌ Non | ❌ Non | ✅ Oui |
| Utilise NOW() | ❌ Non | ✅ Oui | ✅ Oui |
| Utilise random() | ❌ Non | ❌ Non | ✅ Oui |
| Utilisable dans index | ✅ Oui | ❌ Non | ❌ Non |
| Mise en cache du résultat | ✅ Maximum | ❌ Aucune | |
| Optimisation par le planificateur | ✅ Maximum | ❌ Minimale | |
| Comportement par défaut | Non | Non | ✅ Oui (si non spécifié) |
| Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
Comparons l'exécution de la même logique avec différentes volatilités :
-- Version IMMUTABLE
CREATE FUNCTION calcul_immutable(x INTEGER)
RETURNS INTEGER
LANGUAGE SQL
IMMUTABLE
AS $$
SELECT x * 2;
$$;
-- Version VOLATILE (par défaut)
CREATE FUNCTION calcul_volatile(x INTEGER)
RETURNS INTEGER
LANGUAGE SQL
VOLATILE
AS $$
SELECT x * 2;
$$;
-- Test sur une grande table
EXPLAIN ANALYZE
SELECT calcul_immutable(5) FROM generate_series(1, 1000000);
-- Résultat : ~50ms (fonction appelée 1 seule fois, résultat mis en cache)
EXPLAIN ANALYZE
SELECT calcul_volatile(5) FROM generate_series(1, 1000000);
-- Résultat : ~250ms (fonction appelée 1 million de fois !)Différence de performance : 5× plus rapide avec IMMUTABLE !
┌─────────────────────────────────────────────────────────┐
│ Requête : SELECT ma_fonction(5) FROM table (1M lignes) │
└─────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
IMMUTABLE VOLATILE
│ │
▼ ▼
┌──────────┐ ┌─────────────┐
│ Calcul │ │ Calcul 1 │
│ 1 fois │ │ Calcul 2 │
│ │ │ Calcul 3 │
│ Cache → │ │ ... │
│ Réutilise│ │ Calcul 1M │
└──────────┘ └─────────────┘
Résultat Résultat
instantané 1M calculs
Ma fonction modifie-t-elle des données (INSERT/UPDATE/DELETE) ?
│
├─ Oui ──► 🔴 VOLATILE
│
└─ Non ──► Ma fonction lit-elle des données de tables ?
│
├─ Non ──► Ma fonction dépend-elle de l'heure (NOW()) ?
│ │
│ ├─ Non ──► 🟢 IMMUTABLE
│ └─ Oui ──► 🟡 STABLE
│
└─ Oui ──► Ma fonction utilise-t-elle random() ou nextval() ?
│
├─ Oui ──► 🔴 VOLATILE
└─ Non ──► 🟡 STABLE
Question 1 : Avec les mêmes arguments, ma fonction retourne-t-elle TOUJOURS exactement le même résultat, peu importe quand ?
- ✅ Oui → Candidat pour IMMUTABLE
- ❌ Non → Continuer
Question 2 : Ma fonction accède-t-elle à des données en base ?
- ✅ Oui → Au minimum STABLE
- ❌ Non → Continuer
Question 3 : Ma fonction modifie-t-elle des données ou a-t-elle des effets de bord ?
- ✅ Oui → VOLATILE obligatoire
- ❌ Non → Continuer
Question 4 : Ma fonction utilise-t-elle NOW(), CURRENT_DATE, CURRENT_USER ?
- ✅ Oui → STABLE (ces fonctions sont STABLE)
- ❌ Non → IMMUTABLE probable
Question 5 : Ma fonction utilise-t-elle random(), nextval(), currval() ?
- ✅ Oui → VOLATILE obligatoire
- ❌ Non → Vérifier si IMMUTABLE ou STABLE
CREATE FUNCTION calculer_remise_fixe(montant NUMERIC)
RETURNS NUMERIC
LANGUAGE SQL
IMMUTABLE -- ✅ Formule mathématique pure
AS $$
SELECT CASE
WHEN montant > 1000 THEN montant * 0.10
WHEN montant > 500 THEN montant * 0.05
ELSE 0
END;
$$;
-- Justification :
-- - Ne lit pas de tables
-- - Formule déterministe
-- - Résultat dépend uniquement des argumentsCREATE FUNCTION obtenir_taux_tva_actuel()
RETURNS NUMERIC
LANGUAGE SQL
STABLE -- ✅ Lit une table mais stable dans la transaction
AS $$
SELECT taux FROM parametres_fiscaux WHERE actif = true LIMIT 1;
$$;
-- Justification :
-- - Lit une table (pas IMMUTABLE)
-- - Résultat stable pendant la transaction
-- - Ne modifie rien (pas VOLATILE)CREATE FUNCTION generer_id_unique()
RETURNS UUID
LANGUAGE SQL
VOLATILE -- ✅ Génération aléatoire, jamais pareil
AS $$
SELECT gen_random_uuid();
$$;
-- Justification :
-- - Utilise une fonction aléatoire
-- - Résultat différent à chaque appel
-- - Non-déterministe par natureCREATE FUNCTION utilisateur_est_actif(user_id INTEGER)
RETURNS BOOLEAN
LANGUAGE SQL
STABLE -- ✅ Lecture de table
AS $$
SELECT actif FROM utilisateurs WHERE id = user_id;
$$;
-- Justification :
-- - Lit une table (pas IMMUTABLE)
-- - Résultat constant pendant la transaction
-- - Aucune modificationCREATE FUNCTION enregistrer_visite(page TEXT)
RETURNS VOID
LANGUAGE plpgsql
VOLATILE -- ✅ Modifie des données
AS $$
BEGIN
INSERT INTO logs_visites(page, timestamp)
VALUES (page, NOW());
END;
$$;
-- Justification :
-- - Effectue un INSERT (modification)
-- - A un effet de bord visible
-- - Change l'état de la base-- ❌ MAUVAIS : DANGER !
CREATE FUNCTION prix_produit(produit_id INTEGER)
RETURNS NUMERIC
LANGUAGE SQL
IMMUTABLE -- ❌ FAUX ! Lit une table !
AS $$
SELECT prix FROM produits WHERE id = produit_id;
$$;
-- Problème : Si le prix change dans la table, PostgreSQL
-- peut retourner l'ancienne valeur depuis le cache !
-- ✅ CORRECT :
CREATE FUNCTION prix_produit(produit_id INTEGER)
RETURNS NUMERIC
LANGUAGE SQL
STABLE -- ✅ Lit une table = STABLE
AS $$
SELECT prix FROM produits WHERE id = produit_id;
$$;-- ⚠️ PAS OPTIMAL : Par défaut = VOLATILE
CREATE FUNCTION doubler(x INTEGER)
RETURNS INTEGER
LANGUAGE SQL
AS $$
SELECT x * 2;
$$;
-- Cette fonction est VOLATILE alors qu'elle pourrait être IMMUTABLE !
-- Impact : Performances dégradées
-- ✅ CORRECT : Spécifier explicitement
CREATE FUNCTION doubler(x INTEGER)
RETURNS INTEGER
LANGUAGE SQL
IMMUTABLE -- Déclaration explicite
AS $$
SELECT x * 2;
$$;-- ❌ MAUVAIS : Comportement imprévisible
CREATE FUNCTION nombre_aleatoire_entre(min INT, max INT)
RETURNS INTEGER
LANGUAGE SQL
STABLE -- ❌ FAUX ! random() n'est pas stable !
AS $$
SELECT floor(random() * (max - min + 1) + min)::INTEGER;
$$;
-- ✅ CORRECT :
CREATE FUNCTION nombre_aleatoire_entre(min INT, max INT)
RETURNS INTEGER
LANGUAGE SQL
VOLATILE -- random() est VOLATILE
AS $$
SELECT floor(random() * (max - min + 1) + min)::INTEGER;
$$;-- ❌ MAUVAIS : Contradiction !
CREATE FUNCTION est_date_future(date_test DATE)
RETURNS BOOLEAN
LANGUAGE SQL
IMMUTABLE -- ❌ FAUX ! NOW() n'est pas immuable !
AS $$
SELECT date_test > CURRENT_DATE;
$$;
-- ✅ CORRECT :
CREATE FUNCTION est_date_future(date_test DATE)
RETURNS BOOLEAN
LANGUAGE SQL
STABLE -- NOW() et CURRENT_DATE sont STABLE
AS $$
SELECT date_test > CURRENT_DATE;
$$;-- Voir la volatilité de toutes vos fonctions
SELECT
proname AS nom_fonction,
CASE provolatile
WHEN 'i' THEN 'IMMUTABLE'
WHEN 's' THEN 'STABLE'
WHEN 'v' THEN 'VOLATILE'
END AS volatilite
FROM pg_proc
WHERE pronamespace = 'public'::regnamespace
ORDER BY proname; \df+ ma_fonction -- Dans psql, affiche tous les détails
-- Ou en SQL :
SELECT
pg_get_functiondef('ma_fonction'::regproc);-- ✅ BON : Clair et explicite
CREATE FUNCTION ma_fonction(x INTEGER)
RETURNS INTEGER
LANGUAGE SQL
IMMUTABLE -- Déclaration claire
AS $$ ... $$;
-- ❌ ÉVITER : Implicite = confusion
CREATE FUNCTION ma_fonction(x INTEGER)
RETURNS INTEGER
LANGUAGE SQL
AS $$ ... $$; -- Quelle est la volatilité ? On ne sait pas au premier coup d'œil -- Les fonctions IMMUTABLE offrent les meilleures performances
-- Essayez toujours de concevoir vos fonctions pour qu'elles soient IMMUTABLECREATE FUNCTION calculer_montant(...)
RETURNS NUMERIC
LANGUAGE plpgsql
STABLE -- STABLE car lit la table tarifs qui peut changer entre transactions
AS $$
BEGIN
-- Récupère le tarif actuel depuis la base
...
END;
$$;
COMMENT ON FUNCTION calculer_montant IS
'Calcule le montant en appliquant le tarif actuel.
STABLE car dépend de la table tarifs.';-- Créez deux versions et comparez
EXPLAIN ANALYZE SELECT ma_fonction_immutable(x) FROM grande_table;
EXPLAIN ANALYZE SELECT ma_fonction_volatile(x) FROM grande_table;
-- Mesurez la différence et choisissez en conséquence-- Si vous modifiez une fonction, revérifiez sa volatilité
-- Une fonction IMMUTABLE qui commence à lire une table
-- doit devenir STABLE-- ✅ POSSIBLE : Fonction IMMUTABLE
CREATE FUNCTION nom_complet_normalise(prenom TEXT, nom TEXT)
RETURNS TEXT
LANGUAGE SQL
IMMUTABLE
AS $$
SELECT upper(trim(prenom || ' ' || nom));
$$;
CREATE INDEX idx_nom_normalise
ON clients(nom_complet_normalise(prenom, nom));
-- ✅ Succès !
-- ❌ IMPOSSIBLE : Fonction STABLE
CREATE FUNCTION prix_avec_tva_actuelle(prix NUMERIC)
RETURNS NUMERIC
LANGUAGE SQL
STABLE -- Lit le taux de TVA depuis une table
AS $$
SELECT prix * (1 + obtenir_taux_tva());
$$;
CREATE INDEX idx_prix_tva
ON produits(prix_avec_tva_actuelle(prix));
-- ❌ ERREUR : functions in index must be marked IMMUTABLEUn index doit être déterministe :
- Si
f(x) = yaujourd'hui, alorsf(x)doit toujours égalery - Sinon, l'index devient incohérent avec les données réelles
┌─────────────────────────────────────────────────────────────────┐
│ CHOISIR LA VOLATILITÉ │
├─────────────────────────────────────────────────────────────────┤
│
│ 🟢 IMMUTABLE
│ ├─ Résultat : Toujours identique
│ ├─ Accès DB : NON
│ ├─ Modif DB : NON
│ ├─ Cache : Maximum
│ ├─ Index : OUI
│ └─ Perf : ⭐⭐⭐⭐⭐
│
│ 🟡 STABLE
│ ├─ Résultat : Identique dans la transaction
│ ├─ Accès DB : OUI (lecture)
│ ├─ Modif DB : NON
│ ├─ Cache : Par transaction
│ ├─ Index : NON
│ └─ Perf : ⭐⭐⭐⭐
│
│ 🔴 VOLATILE
│ ├─ Résultat : Peut changer à chaque appel
│ ├─ Accès DB : OUI (tout)
│ ├─ Modif DB : OUI
│ ├─ Cache : Aucun
│ ├─ Index : NON
│ └─ Perf : ⭐⭐⭐
│ │
└─────────────────────────────────────────────────────────────────┘
Pour chacune des fonctions suivantes, réfléchissez à la volatilité appropriée :
- Une fonction qui calcule l'âge à partir d'une date de naissance
- Une fonction qui retourne le nombre de commandes d'un client
- Une fonction qui génère un mot de passe aléatoire
- Une fonction qui convertit des euros en dollars avec un taux fixe de 1.10
- Une fonction qui incrémente un compteur de visites
Réponses :
- STABLE (utilise CURRENT_DATE)
- STABLE (lit une table)
- VOLATILE (aléatoire)
- IMMUTABLE (calcul pur avec constante)
- VOLATILE (modifie des données)
"Quand vous hésitez entre deux catégories, choisissez toujours la plus restrictive (VOLATILE), puis optimisez vers STABLE ou IMMUTABLE une fois que vous êtes certain du comportement."
- Ma fonction ne dépend que de ses arguments → IMMUTABLE ?
- Ma fonction lit des données mais ne les modifie pas → STABLE ?
- Ma fonction modifie des données ou a des effets de bord → VOLATILE
- Ma fonction utilise random(), nextval() → VOLATILE
- Ma fonction utilise NOW(), CURRENT_DATE → STABLE
- J'ai déclaré explicitement la volatilité → ✅
- ❌ Déclarer IMMUTABLE une fonction qui lit des tables
- ❌ Ne pas spécifier de volatilité (défaut = VOLATILE)
- ❌ Ignorer l'impact sur les performances
- ❌ Oublier que NOW() est STABLE, pas IMMUTABLE
La volatilité est un concept fondamental dans PostgreSQL qui influence directement :
- Les performances de vos requêtes
- Les possibilités d'optimisation offertes au planificateur
- La création d'index sur des expressions
En comprenant et en appliquant correctement les catégories de volatilité, vous permettez à PostgreSQL de :
- ✅ Mettre en cache les résultats quand c'est sûr
- ✅ Créer des index fonctionnels efficaces
- ✅ Optimiser vos requêtes de manière agressive
Principes directeurs :
- IMMUTABLE pour les fonctions pures (mathématiques, conversions)
- STABLE pour les fonctions de lecture (lookup, configuration)
- VOLATILE pour les fonctions avec effets de bord (modification, aléatoire)
Toujours déclarer explicitement la volatilité pour éviter les surprises et optimiser les performances !
🎓 Prochaines étapes dans le tutoriel :
- 15.3. Procédures stockées et gestion transactionnelle (CALL, COMMIT/ROLLBACK)
- 15.4. Triggers (BEFORE, AFTER, INSTEAD OF)
- 15.5. Event Triggers : Surveillance DDL
💡 Pour aller plus loin :
- Documentation officielle PostgreSQL : Function Volatility Categories
- Testez différentes volatilités sur vos propres fonctions et mesurez l'impact avec EXPLAIN ANALYZE
⏭️ Procédures stockées et gestion transactionnelle (CALL, COMMIT/ROLLBACK)