SQL Server 2016 : fin de support étendu le 14 juillet 2026
La politique de cycle de vie Microsoft prévoit dix ans de support pour chaque version de SQL Server : cinq ans de support standard, puis cinq ans de support étendu. Pour SQL Server 2016, la date de fin de support étendu est le 14 juillet 2026. Passée cette date, Microsoft ne publie plus de mises à jour de sécurité, de correctifs critiques, ni de mises à jour techniques pour cette version. Une instance SQL Server 2016 exposée sur un réseau après cette date accumule des vulnérabilités non corrigées. Les obligations réglementaires — PCI-DSS, RGPD, ISO 27001 — exigent en général que les composants d'infrastructure reçoivent les correctifs de sécurité disponibles. Rester sur SQL Server 2016 après juillet 2026 constitue donc un écart documentable lors d'un audit.
Pourquoi choisir PostgreSQL plutôt que la mise à jour SQL Server ou Azure SQL
- Coût de licence nul : PostgreSQL est sous licence PostgreSQL (permissive, similaire BSD), sans CAL, sans édition Enterprise à payer séparément.
- Contrôle total de l'infrastructure : sur un VPS dédié, vous gérez les versions, les extensions, les paramètres de mémoire et les sauvegardes sans dépendre d'un fournisseur cloud.
- SQL Server 2022 corrige la dette de version mais maintient les coûts de licence et lie l'organisation à l'écosystème Microsoft.
- Azure SQL Managed Instance simplifie la migration mais introduit un coût mensuel récurrent, une dépendance à Azure, et des contraintes réseau supplémentaires.
- PostgreSQL supporte les mêmes fonctionnalités avancées : fenêtres analytiques, CTE récursives, JSON natif, partitionnement de table, réplication logique.
- L'écosystème d'outillage est large : pgAdmin, DBeaver, Metabase, PostgREST, et la plupart des ORM (Entity Framework, Hibernate, SQLAlchemy, ActiveRecord) supportent PostgreSQL.
- Les distributions Linux majeures packagent PostgreSQL avec des mises à jour de sécurité régulières incluses dans le contrat de maintenance de la distribution.
Audit préliminaire : ce qui migre bien, ce qui demande du travail
Avant d'écrire la moindre commande de migration, un audit du schéma existant est indispensable. Les éléments qui migrent sans friction sont les tables simples avec des types standard, les contraintes PRIMARY KEY, FOREIGN KEY, UNIQUE et NOT NULL, les index B-tree classiques, les vues sans syntaxe T-SQL propriétaire, et les séquences. Les éléments qui demandent un travail explicite sont les procédures stockées et les fonctions T-SQL (syntaxe incompatible, à réécrire en PL/pgSQL ou à supprimer), les triggers qui utilisent les tables virtuelles INSERTED et DELETED (équivalent PostgreSQL : NEW et OLD), les colonnes ROWVERSION / TIMESTAMP utilisées pour la détection de concurrence optimiste, les colonnes calculées (COMPUTED COLUMNS), les hint de verrouillage (WITH (NOLOCK), WITH (UPDLOCK)), et les tables temporaires globales (##temp). Un inventaire SQL Server peut être obtenu par requête sur INFORMATION_SCHEMA et sur les vues système sys.objects, sys.procedures, sys.triggers.
Correspondance des types SQL Server → PostgreSQL
Faites défiler le tableau
| Type SQL Server | Type PostgreSQL | Remarque |
|---|---|---|
| DATETIME2 | TIMESTAMPTZ | Stocker les timestamps avec fuseau horaire ; DATETIME2 est sans timezone, prévoir la convention UTC explicitement |
| UNIQUEIDENTIFIER | UUID | PostgreSQL supporte nativement le type UUID ; extension `uuid-ossp` ou `gen_random_uuid()` pour la génération |
| NVARCHAR(MAX) / NTEXT | TEXT | PostgreSQL stocke toutes les chaînes en UTF-8 ; pas de distinction NVARCHAR/VARCHAR ni de limite MAX séparée |
| BIT | BOOLEAN | Les valeurs `0`/`1` deviennent `false`/`true` ; attention aux colonnes BIT utilisées comme entiers dans les calculs |
| MONEY / SMALLMONEY | NUMERIC(19,4) | MONEY SQL Server est un entier mis à l'échelle, NUMERIC est exact ; comportement identique pour les calculs financiers |
| INT IDENTITY(1,1) | SERIAL ou GENERATED ALWAYS AS IDENTITY | SERIAL est un raccourci vers une séquence ; GENERATED ALWAYS AS IDENTITY est le standard SQL:2003, préférable pour les nouvelles tables |
| ROWVERSION / TIMESTAMP | bytea + trigger ou xmin | Pas d'équivalent direct ; la colonne système `xmin` permet la détection de concurrence optimiste dans la plupart des cas |
Incompatibilités T-SQL courantes et leurs équivalents PostgreSQL
Plusieurs constructions T-SQL courantes n'ont pas d'équivalent direct en SQL standard et nécessitent une réécriture.
TOP N sans ORDER BY : SQL Server accepte SELECT TOP 10 * FROM t sans clause d'ordre. PostgreSQL requiert LIMIT 10 ; si un ordre défini est important, ajouter ORDER BY explicitement.
ISNULL(expr, valeur) : la fonction T-SQL ISNULL a pour équivalent standard COALESCE(expr, valeur). Le comportement est identique pour les cas courants.
GETDATE() et GETUTCDATE() : ces fonctions deviennent respectivement NOW() (timestamp avec timezone de la session) et NOW() AT TIME ZONE 'UTC' ou CURRENT_TIMESTAMP.
DATEDIFF et DATEADD : PostgreSQL utilise l'arithmétique d'intervalles : date + INTERVAL '7 days', date2 - date1 retourne un INTERVAL.
HINT WITH (NOLOCK) : ce hint demande une lecture sans verrou partagé (lectures non répétables). PostgreSQL ne supporte pas ce hint ; le niveau d'isolation READ COMMITTED (défaut) couvre la plupart des cas d'usage. Pour les rapports analytiques qui tolèrent des données légèrement obsolètes, une réplique en lecture est la solution structurelle.
PRINT dans les procédures : utiliser RAISE NOTICE 'message' en PL/pgSQL.
GO comme séparateur de batch : ce séparateur n'existe pas en PostgreSQL. Les scripts SQL doivent être restructurés en transactions ou en blocs DO $$...$$ séparés.
Plan de migration en 8 étapes
Inventaire et audit du schéma : exporter la liste de toutes les tables, colonnes avec types, index, contraintes, procédures stockées, fonctions, triggers et vues. Identifier et prioriser les objets qui nécessitent une réécriture manuelle.
Mise en place du VPS PostgreSQL cible : installer PostgreSQL 16 sur le VPS, configurer
postgresql.conf(mémoire partagée, connexions, WAL), créer l'utilisateur applicatif avec le minimum de privilèges, activer TLS pour les connexions distantes.Conversion du schéma avec pgloader ou manuellement :
pgloader mssql://user:pass@host/db postgresql://user:pass@vps/dbmigre le schéma et les données en une commande. Pour les schémas complexes, exporter le DDL SQL Server et l'adapter manuellement, en appliquant la table de correspondance des types.Validation du schéma migré : vérifier le nombre de tables, le compte de lignes par table, l'intégrité référentielle (
FOREIGN KEY), et les index. Comparer les types réels avec les types attendus viainformation_schema.columns.Réécriture des procédures stockées et triggers : chaque objet T-SQL doit être réécrit en PL/pgSQL ou déplacé dans la couche applicative. Tester chaque procédure indépendamment avec des jeux de données représentatifs.
Adaptation du code applicatif : rechercher dans le code source toutes les chaînes SQL littérales et les requêtes paramétrées qui utilisent des constructions T-SQL propriétaires. Mettre à jour le driver de connexion (passer de
mssqlàpg,psycopg2,npgsql, ou le driver approprié à la stack).Tests de charge et de non-régression : exécuter la suite de tests de régression complète contre la base PostgreSQL de staging. Utiliser
pgbenchpour valider les performances sous charge. Comparer les plans d'exécution des requêtes critiques (EXPLAIN ANALYZE) entre SQL Server et PostgreSQL.Bascule en fenêtre de maintenance : planifier une fenêtre (nuit ou week-end), mettre l'application en mode maintenance, exécuter une migration différentielle finale (données insérées depuis la migration initiale), pointer le DSN applicatif vers PostgreSQL, valider les fonctions critiques, et lever la maintenance. Conserver SQL Server en lecture seule pendant 48 heures pour permettre un rollback rapide si nécessaire.
Configurer PostgreSQL 16 sur un VPS pour la production
Une installation PostgreSQL par défaut n'est pas calibrée pour la production. Les paramètres suivants dans postgresql.conf sont un point de départ pour un VPS avec 4 Go de RAM.
shared_buffers : fixer à 25 % de la RAM disponible, soit 1GB pour 4 Go. Ce paramètre détermine la mémoire allouée au cache interne de PostgreSQL.
effective_cache_size : fixer à 75 % de la RAM totale, soit 3GB. Ce paramètre informe le planificateur de requêtes de la mémoire disponible pour le cache du système d'exploitation.
work_mem : 16MB à 64MB selon la complexité des requêtes de tri. Ce paramètre s'applique par opération de tri et par connexion ; une valeur trop élevée sur une instance avec de nombreuses connexions simultanées épuise la mémoire.
maintenance_work_mem : 256MB pour les opérations de maintenance (VACUUM, CREATE INDEX).
max_connections : 100 est suffisant pour la plupart des applications qui utilisent un pool de connexions. Si l'application ouvre une connexion par requête sans pool, utiliser PgBouncer en amont.
Pour pg_hba.conf, restreindre les connexions distantes à l'adresse IP du serveur applicatif et exiger TLS : hostssl all appuser 10.0.0.5/32 scram-sha-256.
Activer TLS en générant un certificat (Let's Encrypt ou certificat interne) et en configurant ssl = on, ssl_cert_file et ssl_key_file dans postgresql.conf.
Piège des procédures stockées et des triggers T-SQL
Les procédures stockées T-SQL sont le principal point de friction d'une migration SQL Server vers PostgreSQL. T-SQL est un dialecte propriétaire avec des structures de contrôle, une gestion des curseurs, et une gestion des erreurs (blocs TRY...CATCH) qui n'ont pas d'équivalent direct en PL/pgSQL.
Les curseurs FAST_FORWARD de SQL Server deviennent des curseurs FOR en PL/pgSQL, mais les performances sont similaires et l'usage des curseurs doit dans tous les cas être questionné : une requête ensembliste remplace presque toujours un curseur de manière plus efficace.
Le bloc TRY...CATCH devient BEGIN...EXCEPTION WHEN OTHERS THEN en PL/pgSQL.
Les tables temporaires locales (#temp) correspondent aux tables temporaires de session PostgreSQL créées avec CREATE TEMPORARY TABLE. Les tables temporaires globales (##temp) n'ont pas d'équivalent direct — elles doivent être remplacées par des tables permanentes avec une gestion explicite du cycle de vie, ou par des tables dans un schéma dédié avec des politiques de nettoyage.
Les triggers qui référencent INSERTED et DELETED (tables virtuelles SQL Server) doivent être réécrits pour utiliser NEW et OLD en PostgreSQL. La structure de déclenchement est aussi différente : PostgreSQL distingue les triggers BEFORE et AFTER, et les triggers FOR EACH ROW des triggers FOR EACH STATEMENT.
Une approche pragmatique consiste à déplacer la logique des procédures stockées dans la couche applicative lors de la migration, ce qui simplifie les tests et réduit le couplage entre l'application et la base de données.
Test de charge avant bascule : pgbench et comparaison des plans de requêtes
Avant de basculer en production, valider les performances avec pgbench. Initialiser une base de test représentative : pgbench -i -s 50 nom_base crée un schéma de benchmark avec 5 millions de lignes. Lancer une simulation de charge : pgbench -c 20 -j 4 -T 60 nom_base simule 20 connexions parallèles pendant 60 secondes et rapporte les transactions par seconde. Pour les requêtes critiques de l'application, comparer les plans d'exécution entre SQL Server (SET STATISTICS IO ON; SELECT ...) et PostgreSQL (EXPLAIN (ANALYZE, BUFFERS) SELECT ...). Les points d'attention sont les scans de table complets inattendus (index non migré ou statistiques non calculées — lancer ANALYZE après la migration), et les jointures NESTED LOOP sur de grands volumes qui bénéficieraient d'un HASH JOIN. Mettre à jour les statistiques après la migration initiale avec ANALYZE nom_table ou VACUUM ANALYZE pour l'ensemble de la base.
Dépannage — erreurs courantes lors d'une migration SQL Server vers PostgreSQL
Plusieurs catégories d'erreurs reviennent systématiquement lors des premières migrations.
ERROR: column is of type uuid but expression is of type character varying : une colonne UNIQUEIDENTIFIER migrée en UUID reçoit une chaîne formatée comme un UUID mais sans cast explicite. Soit adapter la requête applicative pour envoyer un vrai type UUID, soit ajouter un cast implicite dans PostgreSQL (CREATE CAST), soit migrer la colonne en TEXT si la sémantique UUID n'est pas exploitée.
ERROR: operator does not exist: boolean = integer : une colonne BIT migrée en BOOLEAN est comparée à 0 ou 1. Remplacer col = 1 par col = true ou simplement col dans les clauses WHERE.
ERROR: function isnull(character varying, text) does not exist : remplacer toutes les occurrences de ISNULL par COALESCE.
ERROR: syntax error at or near "TOP" : la syntaxe SELECT TOP N n'existe pas en PostgreSQL. Remplacer par SELECT ... LIMIT N.
Dépassements silencieux sur les colonnes MONEY → NUMERIC : vérifier que les valeurs extrêmes de la table source tiennent dans la précision cible NUMERIC(19,4). SQL Server MONEY a une plage de ±922 337 203 685 477,5807 ; NUMERIC(19,4) couvre ±999 999 999 999 999,9999, ce qui est suffisant dans la quasi-totalité des cas.
Pertes de données sur les colonnes DATETIME sans timezone : SQL Server DATETIME et DATETIME2 ne stockent pas le fuseau horaire. Si la base de données était alimentée par des applications dans plusieurs fuseaux, les timestamps migrés en TIMESTAMPTZ sans conversion préalable seront interprétés comme UTC. Définir une convention (UTC ou heure locale) et appliquer un AT TIME ZONE lors de la migration si nécessaire.
Vérifier la migration et remettre les utilisateurs
Après la bascule, une checklist de vérification structurée réduit le risque de remettre les utilisateurs sur une base incomplète.
Vérifier le compte de lignes pour toutes les tables critiques : une requête sur information_schema.tables avec un COUNT(*) par table, comparée aux chiffres relevés sur SQL Server avant la migration.
Vérifier l'intégrité référentielle : SELECT * FROM pg_constraint WHERE contype = 'f' liste toutes les contraintes de clé étrangère ; tenter une insertion qui violerait une contrainte pour confirmer qu'elle est active.
Vérifier les index : SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' liste tous les index créés. Comparer avec la liste des index SQL Server.
Tester les fonctions critiques de l'application : parcours d'achat, authentification, rapports, exports — au minimum les parcours qui touchent aux tables les plus volumineuses et aux requêtes les plus complexes.
Surveiller les logs PostgreSQL pendant les premières 24 heures : tail -f /var/log/postgresql/postgresql-*.log. Les erreurs de type character varying vs uuid, les timeouts de connexion, ou les plans de requête sous-optimaux se manifestent généralement dans les premières heures de trafic réel.
Conserver SQL Server en lecture seule pendant 48 heures avec un accès restreint à l'équipe technique. Le rollback consiste à pointer le DSN applicatif vers SQL Server et à rejouer les données écrites dans PostgreSQL pendant la fenêtre de bascule.