Migrazione database Access a SQL Server: guida
Matteo Migliore

Matteo Migliore è un imprenditore e architetto software con oltre 27 anni di esperienza nello sviluppo di soluzioni basate su .NET e nell'evoluzione di architetture applicative per imprese e organizzazioni di alto profilo.

Ha guidato progetti enterprise, formato centinaia di sviluppatori e aiutato aziende di ogni dimensione a semplificare la complessità trasformando il software in guadagni per il business.

Il file si chiama Gestionale_ordini_v14_DEFINITIVO.accdb e pesa 1,7 GB. Lo usano dodici persone, tutte nella stessa rete, e da due mesi succede una cosa nuova: il lunedì mattina qualcuno apre una maschera e Access risponde che il database "ha bisogno di essere compattato e riparato". Il titolare di un'azienda di serramenti del Veneto mi ha scritto una riga sola: "Quanto ci vuole per metterlo su SQL Server e tenerci le maschere che abbiamo?".

La domanda ha una risposta precisa, e non è né "una settimana" né "è meglio buttare tutto". Spostare i dati di Access su SQL Server è un lavoro che si può fare in modo ordinato, senza fermare l'azienda per più di un weekend, e che toglie in un colpo solo i tre problemi che fanno paura: il limite dei 2 GB, la corruzione del file e l'accesso da fuori ufficio. Ma ha anche trappole che fanno rallentare il gestionale invece di velocizzarlo, e le trovi quasi tutte il lunedì dopo.

In questa guida trovi che cosa significa davvero migrare un database Access, quando conviene e quando no, le quattro strade possibili con i costi, l'inventario da fare prima di toccare qualsiasi cosa, il passo per passo con SQL Server Migration Assistant, la mappatura dei tipi di dato, come si ricollegano le maschere, perché le query diventano lente e come si sistemano, i permessi, il piano del fine settimana di passaggio e gli errori che vedo ripetersi. Alla fine c'è una regola in cinque righe per decidere se farlo da soli o chiedere aiuto.

Scrivo software dal 1999 e ho tirato fuori dati da più file Access di quanti ne possa contare: gestionali di officine, di studi professionali, di aziende di trasporto. Qui non ci sono teorie: ci sono i numeri e i comandi che uso io. Se invece il tuo dubbio è se sostituire del tutto Access con un altro gestionale, quello è un altro discorso, e lo trovi in sostituire Access con un gestionale.

Quando i dati di Access sono già su SQL Server e resta da rinnovare il sito web collegato, il passo successivo è la migrazione da ASP classico a ASP.NET Core.

Che cosa significa migrare un database Access a SQL Server?

Migrare un database Access a SQL Server significa spostare le tabelle e i dati nel motore di SQL Server, lasciando che Access resti, se vuoi, l'interfaccia con cui gli utenti lavorano. I dati vanno in un posto robusto; le maschere, i report e le macro continuano a funzionare collegandosi alle tabelle sul server.

Access è due cose in una

Il primo equivoco da togliere è questo. Un file .accdb contiene due livelli che nessuno distingue finché non si rompe qualcosa. Il primo è il motore dei dati: tabelle, indici, relazioni, query. Il secondo è l'applicazione: maschere, report, macro, codice VBA. In un file solo, condiviso da dodici persone sulla rete, tutti aprono la stessa copia del motore e dell'applicazione contemporaneamente. È il motivo per cui il file si corrompe: più persone scrivono sullo stesso file attraverso la rete, e un cavo staccato, uno sleep del portatile o una VPN che cade a metà scrittura bastano a lasciare le pagine in uno stato incoerente.

La migrazione separa i due livelli. I dati vanno in SQL Server. L'applicazione resta in un file Access piccolo, che contiene solo maschere e report e che ogni utente tiene in locale. Questo schema si chiama front-end e back-end, e Microsoft lo raccomanda da più di vent'anni. Cambia tutto: il file che si corrompeva non esiste più, perché i dati li scrive il server e non dodici computer.

Prima: dodici utenti scrivono sullo stesso file Access in rete. Dopo: dodici front-end Access parlano con SQL Server, che scrive i dati

Che cosa cambia per chi lavora ogni giorno

Se la migrazione è fatta bene, quasi niente. Le maschere sono le stesse, i pulsanti sono gli stessi, la persona che inserisce ordini dalle otto alle cinque non nota nulla, tranne che il lunedì il file non si rompe e che certe ricerche sono più veloci. Se è fatta male, il gestionale diventa lento in modo misterioso e ti ritrovi a rimpiangere il file da 1,7 GB. La differenza fra le due cose sta quasi sempre in quattro punti che vedremo uno per uno: i tipi di dato, le chiavi, le query e il codice VBA.

Che cosa non cambia

SQL Server non scrive un'applicazione al posto tuo. Non rende Access un programma web, non toglie la dipendenza da Windows e dall'installazione di Office sui computer, non sistema il codice VBA scritto nel 2009 da chi non lavora più in azienda. Risolve il problema dei dati, che è il più urgente e spesso il più costoso. Il resto si affronta dopo, con calma, e solo se serve.

Quando conviene davvero passare da Access a SQL Server?

Conviene quando il file si avvicina ai 2 GB, quando si corrompe o rallenta con più utenti, quando serve lavorare da fuori ufficio o quando i dati devono essere protetti e salvati con regolarità. Se nessuno di questi segnali c'è, lasciare Access com'è può essere la scelta giusta.

I segnali che contano

  • La dimensione del file. Un database Access ha un tetto di 2 GB, comprese le tabelle di sistema e lo spazio occupato dagli oggetti cancellati. Dai 1,2 GB in su si lavora vicino al muro: una compattazione sbagliata e non si apre più. Se cresci di 300 MB l'anno, hai due anni, non dieci.
  • Le corruzioni. Un file che chiede la riparazione una volta l'anno è normale. Un file che la chiede ogni mese, o che perde record dopo la riparazione, sta chiedendo di essere spostato.
  • Il numero di utenti. Access regge bene fino a cinque o sei utenti in scrittura, con difficoltà fino a dieci o quindici. Oltre, i blocchi sui record e il traffico di rete fanno il resto.
  • Il lavoro da remoto. Aprire un file Access da una VPN a bassa velocità è il modo più sicuro per corromperlo. Con SQL Server il traffico è fatto di richieste e risposte piccole, non di un file intero trascinato sulla rete.
  • La sicurezza e i salvataggi. Un file in una cartella condivisa ha permessi sulla cartella, non sui dati. Con SQL Server decidi chi legge e chi scrive tabella per tabella, e il salvataggio automatico con registro delle transazioni permette di tornare a un minuto preciso.
  • Le integrazioni. Se un altro programma, un sito o un'app deve leggere quei dati, fargli aprire un file Access è fragile. Con SQL Server basta un'utenza e una stringa di connessione.

Quando non conviene

Se hai tre utenti, un file da 80 MB che non ha mai dato problemi e nessuna esigenza di accesso da fuori, la migrazione è un costo senza ritorno visibile. Meglio spendere quelle giornate per fare una copia di sicurezza automatica e per documentare le tabelle. E se il gestionale è così vecchio che nessuno lo capisce più, e le funzioni che servono sono la metà di quelle che contiene, conviene guardare a un'altra strada, quella che ho analizzato in riscrivere o rifattorizzare il software legacy.

Un numero utile per decidere: quanti minuti perde ogni utente ogni giorno per attese, riavvii e ricerche lente? Dodici persone che perdono quindici minuti al giorno per 220 giorni lavorativi sono 660 ore l'anno. A un costo orario aziendale di 30 euro, sono quasi 20.000 euro che nessuno vede in bilancio. Con questa cifra davanti, la discussione cambia tono.

Quali sono le strade possibili e quanto costano?

Le strade sono quattro: tenere Access com'è con le sole protezioni, spostare i dati su SQL Server tenendo le maschere, costruire un nuovo programma sopra SQL Server, o sostituire tutto con un gestionale. Cambiano il costo, la durata e il rischio, e non sono una scala dove la più cara è la migliore.

Strada A: restare su Access, ma metterlo in sicurezza

Separi front-end e back-end anche senza SQL Server, fai una copia di sicurezza notturna, imposti la compattazione programmata e dai a ciascun utente la sua copia delle maschere. Costa poco, circa tre o quattro giorni di lavoro, e compra uno o due anni. Non toglie il tetto dei 2 GB né l'accesso da remoto.

Strada B: dati su SQL Server, maschere Access (quella di questa guida)

Sposti le tabelle, ricolleghi le maschere, correggi le query che diventano lente. L'interfaccia resta quella che l'azienda conosce. È la strada con il miglior rapporto fra rischio e risultato, e ne parliamo per tutto l'articolo. Per un file con una quarantina di tabelle, sessanta maschere e un centinaio di query serve un lavoro di circa venti-venticinque giorni persona, compresi collaudo e passaggio.

Strada C: nuovo programma sopra SQL Server

Dopo aver spostato i dati, costruisci un'applicazione web o desktop in .NET che li usa al posto delle maschere Access, un modulo per volta. Ti libera da Access e da Office, funziona da qualsiasi luogo, e costa molto di più. È l'approccio a "strangolamento graduale" che descrivo in strangler fig pattern: il vecchio sistema si restringe mentre il nuovo cresce.

Strada D: sostituire con un gestionale

Se le funzioni che ti servono esistono già in un prodotto del mercato, comprarlo costa meno che scrivere. Ma va fatta con numeri alla mano, e li ho riportati in sostituire Access con un gestionale e in quanto costa un gestionale su misura.

Il confronto in una tabella

  • A, metterlo in sicurezza: 3-4 giorni, circa 2.000 euro, compra 1-2 anni, non risolve il tetto dei 2 GB.
  • B, SQL Server con le maschere Access: 20-25 giorni, circa 12.000-15.000 euro, compra 5-8 anni per i dati, risolve corruzione, limite e accesso.
  • C, nuovo programma sopra SQL Server: 120-300 giorni, da 70.000 a 180.000 euro, indipendente da Access, rischio più alto.
  • D, gestionale del mercato: canone o licenza più 30-60 giorni di adattamento, da 20.000 a 90.000 euro nei primi due anni.

Se il tuo file è un pezzo di un sistema più grande da rimettere in sesto, il metodo completo è nella pagina sulla modernizzazione del software legacy.

Le cifre sono ordini di grandezza calcolati con una tariffa di riferimento di 600 euro al giorno, che è un'ipotesi di lavoro e non il mio listino. Servono a capire dove cade la tua situazione, non a firmare un preventivo. Per un preventivo vero servono i numeri del tuo file, e più sotto ti dico quali.

Che cosa si controlla prima di migrare?

Prima di migrare si fa un inventario del file: tabelle, relazioni, query, maschere, report, macro e codice VBA, con l'indicazione di quali oggetti sono davvero in uso. Il novanta per cento dei problemi del lunedì dopo nasce da qualcosa che l'inventario avrebbe mostrato.

Le sei cose da contare

  1. Tabelle e dimensione. Quante sono, quante righe contengono, quali pesano di più. Tre tabelle di solito fanno il novanta per cento del file.
  2. Chiavi primarie. Ogni tabella deve averne una. Le tabelle senza chiave primaria non si possono aggiornare dalle maschere dopo il passaggio a SQL Server, perché Access non sa quale riga stai toccando.
  3. Relazioni e integrità. Quelle dichiarate in Access diventano chiavi esterne. Quelle "solo nella testa di chi ha costruito il file" vanno scoperte e dichiarate, altrimenti i dati sporchi attraversano la migrazione.
  4. Query. Quante sono e quali usano funzioni VBA dentro la query. Sono quelle che dopo si bloccano.
  5. Codice VBA e macro. Quanto codice apre recordset, concatena SQL, usa DLookup o DCount dentro cicli. Sono i punti caldi.
  6. Collegamenti esterni. Fogli Excel, altri file Access, ODBC, cartelle con allegati. Il file raramente vive da solo.

Un modo veloce per fare l'elenco

Access ha una tabella di sistema nascosta, MSysObjects, che elenca tutti gli oggetti del file. Con una query la interroghi, e ottieni in pochi secondi il quadro di cui hai bisogno. Per vederla devi attivare "Mostra oggetti di sistema" nelle opzioni di navigazione.

SELECT Type, Count(*) AS Quanti
FROM MSysObjects
WHERE Name NOT LIKE "MSys*" AND Name NOT LIKE "~*"
GROUP BY Type;

Il campo Type vale 1 per le tabelle locali, 4 per le tabelle collegate via ODBC, 5 per le query, -32768 per le maschere, -32764 per i report e -32761 per i moduli di codice. Dalla risposta capisci subito quanto è grosso il lavoro: cento query e sessanta maschere sono un progetto da qualche settimana, quindici query e dieci maschere sono un lavoro da pochi giorni.

Chiedi chi usa davvero che cosa

Nei file con dieci anni di storia, un terzo degli oggetti non è più usato da nessuno. Maschere di prova, query "provvisorie", report per un cliente di cui non si ricorda il nome. Migrarli costa e non serve. Il modo più sicuro per scoprirlo è chiedere agli utenti e, dove non si riesce, aggiungere per due settimane una riga di registro nell'apertura di ogni maschera. Dopo quindici giorni sai che cosa si apre e che cosa no. Gli oggetti mai aperti si archiviano in un file a parte e non si migrano.

Le due domande che cambiano il preventivo

Quanti utenti scrivono contemporaneamente alle ore di punta? E c'è qualcuno che deve lavorare da fuori ufficio? Con la prima capisci la scelta fra SQL Server Express (gratuito, fino a 10 GB per database) e una versione a pagamento. Con la seconda decidi se i dati vanno su un server in sede, su una macchina virtuale o su Azure SQL.

Come si migra con SQL Server Migration Assistant?

SQL Server Migration Assistant per Access, noto come SSMA, è lo strumento gratuito di Microsoft che legge il file Access, crea le tabelle su SQL Server, copia i dati e converte le query in viste. Si usa in una sequenza fissa: connetti, converti, carica, migra, verifica.

La sequenza, passo per passo

  1. Copia il file. Lavora sempre su una copia e mai sull'originale. Fai una compattazione e riparazione sulla copia prima di iniziare.
  2. Crea il database vuoto su SQL Server con un nome chiaro, per esempio GestioneOrdini, e un'utenza dedicata.
  3. Apri SSMA e crea un progetto. Aggiungi il file Access e connetti il progetto a SQL Server. Controlla le opzioni di mappatura dei tipi (ne parliamo fra poco).
  4. Esegui "Create Report". Prima di convertire, SSMA produce un rapporto: quali oggetti si convertono senza problemi e quali no. Leggilo con calma, è la tua lista dei guai.
  5. Converti lo schema. SSMA crea tabelle, indici, chiavi e relazioni. Controlla gli avvisi: ogni avviso è un tipo o una regola cambiata.
  6. Carica lo schema su SQL Server. Le tabelle esistono ora vuote.
  7. Migra i dati. Il tempo dipende dalla dimensione: un file da 1,7 GB si sposta in 10-30 minuti su una rete interna.
  8. Collega le tabelle in Access. SSMA può farlo da solo: sostituisce le tabelle locali con tabelle collegate che puntano al server.
  9. Verifica. Conta le righe tabella per tabella, confronta le somme di colonne chiave, apri le dieci maschere più usate.

La verifica che salva il progetto

Non fidarti dello strumento sui conteggi. Dopo la migrazione fai tu il controllo: per ogni tabella principale, numero di righe e somma di un campo numerico, sia su Access sia su SQL Server. Se sull'originale hai 184.322 righe d'ordine e un totale di 41.870.215,40 euro, sul nuovo devi avere gli stessi due numeri, al centesimo. Una differenza, anche minuscola, indica un tipo convertito male o un record scartato.

SELECT COUNT_BIG(*) AS Righe,
       SUM(Importo)  AS TotaleImporto,
       MIN(DataOrdine) AS PrimaData,
       MAX(DataOrdine) AS UltimaData
FROM dbo.OrdiniRighe;

Lo stesso conteggio su Access si ottiene con una query di totali sulla tabella originale conservata nella copia. Mettere i due risultati affiancati in un foglio, con una riga per tabella, è la prova scritta che i dati sono arrivati interi. Se un giorno qualcuno chiederà "ma i dati vecchi sono giusti?", la risposta sarà un foglio e non un'opinione.

Quando SSMA non basta

Per file con tabelle molto grandi, dati sporchi da ripulire durante il passaggio o trasformazioni di struttura (spezzare una tabella in due, normalizzare un campo con valori separati da virgola), conviene scrivere la migrazione a mano. In .NET si legge Access con System.Data.OleDb e si scrive su SQL Server con SqlBulkCopy, che carica centinaia di migliaia di righe in pochi secondi. Un esempio compatto, con Microsoft.Data.SqlClient:

using System.Data.OleDb;
using Microsoft.Data.SqlClient;

const string accesso =
    "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\Migrazione\\Ordini.accdb;";
const string server =
    "Server=srv-sql01;Database=GestioneOrdini;Integrated Security=true;TrustServerCertificate=true";

await using var origine = new OleDbConnection(accesso);
await origine.OpenAsync();

await using var comando = new OleDbCommand(
    "SELECT IdOrdine, IdCliente, DataOrdine, Importo FROM Ordini", origine);
await using var lettore = await comando.ExecuteReaderAsync();

await using var destinazione = new SqlConnection(server);
await destinazione.OpenAsync();

using var copia = new SqlBulkCopy(destinazione)
{
    DestinationTableName = "dbo.Ordini",
    BatchSize = 10_000,
    BulkCopyTimeout = 0
};
copia.ColumnMappings.Add("IdOrdine", "IdOrdine");
copia.ColumnMappings.Add("IdCliente", "IdCliente");
copia.ColumnMappings.Add("DataOrdine", "DataOrdine");
copia.ColumnMappings.Add("Importo", "Importo");

await copia.WriteToServerAsync(lettore);

Il provider ACE va installato sulla macchina che esegue la migrazione e deve avere la stessa architettura (32 o 64 bit) del programma che lo usa. È un dettaglio che fa perdere mezza giornata a chi non lo sa. Per un lavoro di questo tipo, mettere dentro la stessa transazione la copia dei dati e il controllo dei conteggi evita di lasciare un database a metà.

Come si traducono i tipi di dato fra Access e SQL Server?

Ogni tipo di Access ha un corrispondente in SQL Server, ma alcune scelte automatiche sono sbagliate: la più frequente riguarda i campi Sì/No, le date e i campi di testo lunghi. Una mappatura rivista a mano prima della migrazione evita i problemi più fastidiosi.

La tabella di corrispondenza che uso

  • Testo breve diventa nvarchar(n) con la lunghezza reale, non 255 per abitudine.
  • Testo lungo (Memo) diventa nvarchar(max). Mai ntext, che è un tipo superato.
  • Numerico intero diventa smallint, int o bigint a seconda dell'intervallo dei valori.
  • Numerico decimale e Valuta diventano decimal(18,2) per gli importi. Il tipo money funziona ma arrotonda in modo che sorprende nei calcoli intermedi.
  • Data/ora diventa datetime2(0) o date. Il vecchio datetime ha un'accuratezza di tre millisecondi e un intervallo ridotto, ed è sconsigliato per i nuovi lavori.
  • Sì/No diventa bit NOT NULL DEFAULT 0.
  • Contatore (AutoNumber) diventa int IDENTITY.
  • Oggetto OLE e allegati diventano varbinary(max), ma di solito conviene tenere i file su disco e salvare nel database solo il percorso.
  • Collegamento ipertestuale diventa nvarchar(2000).
  • ID replica diventa uniqueidentifier.

Il caso dei Sì/No

In Access un campo Sì/No non può essere vuoto: vale vero o falso. In SQL Server, un campo bit può essere NULL e questo crea uno stato a tre valori che le maschere non sanno gestire. Il risultato tipico è una casella di controllo che appare "grigia" o una query di filtro che non trova le righe giuste. La soluzione è dichiarare il campo NOT NULL con valore predefinito 0, e correggere i valori già presenti prima di caricare.

Il caso delle date

Access accetta date come il 30/12/1899 o come lo 0 che usa come "data zero". Se nei tuoi dati ci sono date prima del 1753, il vecchio tipo datetime le rifiuta e la migrazione si ferma con un errore di conversione. Con datetime2 vanno bene, ma la data zero di Access diventa il 30 dicembre 1899 e non un valore vuoto. Cercale prima della migrazione con una query sulle date minime e decidi se vanno trasformate in NULL.

SELECT MIN(DataOrdine) AS DataMinima, MAX(DataOrdine) AS DataMassima
FROM dbo.Ordini
WHERE DataOrdine IS NOT NULL;

Il caso delle chiavi che non esistono

Le tabelle di Access senza chiave primaria si migrano senza errori e poi non si possono modificare dalle maschere. Prima di migrare, aggiungi a ognuna un campo contatore come chiave. Dove la tabella ha già una colonna che identifica la riga (un codice univoco), usala, ma verifica che sia davvero unica: nei file vecchi i duplicati ci sono sempre.

Il marcatore di versione

Quando Access aggiorna una riga di una tabella collegata, deve sapere se qualcun altro l'ha cambiata nel frattempo. SQL Server offre un tipo apposta, rowversion (chiamato anche timestamp), che si aggiorna da solo a ogni modifica. Aggiungere una colonna rowversion a ogni tabella che si modifica da maschera è una delle migliori abitudini che puoi avere.

ALTER TABLE dbo.Clienti ADD VersioneRiga rowversion NOT NULL;
ALTER TABLE dbo.Ordini  ADD VersioneRiga rowversion NOT NULL;

Senza quella colonna, le tabelle con campi decimali o con valori nulli possono produrre il messaggio "Questo record è stato modificato da un altro utente" anche quando sei l'unico a lavorare. È uno degli errori più frequenti del lunedì dopo, e si cura in cinque minuti.

Come si ricollegano le maschere alle tabelle su SQL Server?

Le maschere si ricollegano con tabelle collegate via ODBC: Access vede le tabelle di SQL Server come se fossero locali, e le maschere continuano a funzionare senza modifiche. Il punto delicato è la stringa di connessione e il driver giusto.

Il driver e la connessione senza DSN

Installa su ogni computer il Microsoft ODBC Driver 18 for SQL Server (o la versione corrente che trovi sul sito di Microsoft). Evita il vecchio driver "SQL Server" che è incluso in Windows: è fermo da anni e non supporta le funzioni recenti. Usa una connessione senza DSN, cioè con la stringa scritta nel front-end, così non devi configurare niente a mano su ogni computer.

Il driver 18 cifra la connessione per impostazione predefinita. Se il tuo server ha un certificato valido, tutto funziona. Se usa un certificato autofirmato, la connessione fallisce con un errore sulla catena di certificati: la soluzione corretta è installare il certificato come attendibile sui client, e solo in casi provvisori aggiungere TrustServerCertificate=yes. Lasciare quel parametro per sempre vuol dire accettare qualsiasi server che si finga il tuo.

Un piccolo codice per ricollegare tutte le tabelle

Dopo la migrazione, ogni tabella collegata va ricollegata se cambia il nome del server o del database, per esempio passando dal collaudo alla produzione. Un modulo VBA di poche righe lo fa in un colpo.

Public Sub RicollegaTabelle(ByVal server As String, ByVal database As String)
    Dim td As DAO.TableDef
    Dim cn As String

    cn = "ODBC;Driver={ODBC Driver 18 for SQL Server};" & _
         "Server=" & server & ";Database=" & database & ";" & _
         "Trusted_Connection=Yes;Encrypt=Yes;"

    For Each td In CurrentDb.TableDefs
        If Len(td.Connect) > 0 And Left$(td.Connect, 5) = "ODBC;" Then
            td.Connect = cn
            td.RefreshLink
        End If
    Next td
End Sub

Con l'autenticazione integrata di Windows (Trusted_Connection=Yes) non scrivi password nel front-end. È la scelta giusta in una rete con Active Directory: ogni utente entra con il suo nome e i permessi si gestiscono sul server. Se invece il file viene usato da fuori rete, o da computer non di dominio, serve un'utenza SQL, e la password non va mai lasciata dentro il file Access distribuito agli utenti: si chiede all'avvio e si tiene solo in memoria.

Il problema dei "#Eliminato"

Capita che una tabella collegata, aperta in Access, mostri tutte le righe con la scritta #Eliminato (o #Deleted nella versione inglese). Succede quasi sempre per una di queste cause: manca la chiave primaria, la tabella ha un campo bit che ammette valori nulli, un campo float ha valori che cambiano di pochissimo quando vengono riletti, oppure manca la colonna rowversion. Le tre correzioni sono quelle viste sopra, e la prima cosa da guardare è sempre la definizione della tabella.

Perché dopo la migrazione il gestionale può diventare più lento?

Il gestionale rallenta quando Access scarica sul proprio computer tabelle intere per eseguire query che SQL Server avrebbe risolto in un attimo: succede con funzioni VBA nelle query, con join fra tabelle locali e remote e con filtri che non possono essere inviati al server. La cura è far lavorare il server dove sa lavorare meglio.

Come ragiona Access con le tabelle collegate

Quando una query di Access coinvolge solo tabelle di SQL Server, il motore ACE (Access Connectivity Engine) traduce quello che può in T-SQL e lo manda al server. Ma tutto quello che non sa tradurre lo esegue in locale, dopo aver scaricato le righe. Se la tua query contiene una funzione scritta in VBA, come FormattaCodice([Codice]), Access non può mandarla al server: scarica tutte le righe della tabella, ne applica la funzione una per una e poi filtra. Con 400.000 righe d'ordine, una query che prima impiegava mezzo secondo ne richiede quaranta.

I quattro rimedi, in ordine di efficacia

  1. Viste su SQL Server. La query pesante diventa una vista scritta in T-SQL, e Access la vede come una tabella. Il server fa il lavoro e ritorna solo il risultato.
  2. Query pass-through. Sono query che Access manda al server senza toccarle. Si usano per report complessi, riepiloghi e tutto ciò che sfrutta funzioni di T-SQL. Sono le più veloci, ma sola lettura.
  3. Stored procedure. Per le operazioni che modificano molti dati (chiusura mensile, ricalcolo dei listini) si scrive una procedura sul server e la si chiama da VBA. Un ciclo che aggiorna cinquantamila righe una per una dal front-end può durare dieci minuti; la stessa operazione in una sola istruzione sul server dura un secondo.
  4. Filtri sui record caricati. Le maschere non devono aprirsi su una tabella intera. Una maschera che carica solo gli ordini dell'ultimo mese, o quelli del cliente scelto, trasferisce qualche centinaio di righe invece di duecentomila.

Un esempio: da quaranta secondi a uno

Il report che somma gli ordini per cliente e per mese, in Access, usava una funzione che trasformava il codice cliente. Sul server lo sostituisco con una vista.

CREATE VIEW dbo.vOrdiniPerClienteMese
AS
SELECT c.IdCliente,
       c.RagioneSociale,
       DATEFROMPARTS(YEAR(o.DataOrdine), MONTH(o.DataOrdine), 1) AS Mese,
       COUNT_BIG(*)  AS NumeroOrdini,
       SUM(o.Importo) AS TotaleImporto
FROM dbo.Ordini AS o
INNER JOIN dbo.Clienti AS c ON c.IdCliente = o.IdCliente
GROUP BY c.IdCliente, c.RagioneSociale,
         DATEFROMPARTS(YEAR(o.DataOrdine), MONTH(o.DataOrdine), 1);

Per la ricerca per periodo serve un indice che aiuti il filtro sulla data. Gli indici sono la seconda grande differenza fra Access e SQL Server: Access ne crea qualcuno da solo, SQL Server ne ha bisogno di quelli giusti, e una colonna usata spesso nei filtri senza indice trasforma ogni ricerca in una lettura completa della tabella.

CREATE NONCLUSTERED INDEX IX_Ordini_DataOrdine
ON dbo.Ordini (DataOrdine)
INCLUDE (IdCliente, Importo);

Come trovare le query che rallentano

Non si indovina: si misura. Sul server, il Query Store (attivo per impostazione predefinita nei database creati con SQL Server 2022 in poi) tiene la storia delle query più lente. Sul lato Access, per sapere che cosa viene davvero mandato al server, si usa il registro ODBC o, più semplice, il profilatore di SQL Server in sessione estesa. Dieci minuti di osservazione durante l'uso normale mostrano le cinque query che cambiano la giornata degli utenti.

Il codice VBA che carica troppo

I tre nemici sono sempre gli stessi: DLookup, DCount e DSum dentro cicli o dentro maschere continue, che eseguono una richiesta al server per ogni riga mostrata; i recordset aperti su tabelle intere e poi filtrati con .Filter; il passare a una maschera tutta la tabella invece di una query con parametro. Le correzioni sono banali da scrivere e spesso danno miglioramenti di venti volte: sostituire la funzione con un join nella query di origine, aprire il recordset con una WHERE già dentro l'istruzione, passare i parametri.

Come si gestiscono permessi, sicurezza e salvataggi?

Con SQL Server dai a ogni persona o gruppo solo i permessi che servono, tabella per tabella, e i salvataggi seguono un piano con copie complete, differenziali e del registro delle transazioni. È uno dei vantaggi concreti della migrazione, e quasi nessuno lo sfrutta.

Chi entra e con quali diritti

Nel file Access chi può aprire il file può fare tutto: leggere, cambiare, cancellare, copiare. Con SQL Server si definiscono ruoli. Il modello più semplice funziona per la maggior parte delle aziende: un ruolo di sola lettura per chi consulta, uno di lettura e scrittura per chi lavora sugli ordini, uno amministrativo per chi gestisce anagrafiche e prezzi. Ai ruoli si collegano i gruppi di Active Directory, e il commerciale che cambia reparto cambia gruppo senza che nessuno tocchi il database.

CREATE ROLE RuoloOrdini;
GRANT SELECT, INSERT, UPDATE ON SCHEMA::dbo TO RuoloOrdini;
DENY  DELETE ON dbo.Ordini TO RuoloOrdini;

CREATE USER [AZIENDA\Ufficio Ordini] FOR LOGIN [AZIENDA\Ufficio Ordini];
ALTER ROLE RuoloOrdini ADD MEMBER [AZIENDA\Ufficio Ordini];

Nel gestionale la cancellazione definitiva di un ordine, in genere, non dovrebbe esistere: si annulla, si segna come cancellato, si conserva lo storico. Il comando DENY DELETE lo impone a livello di database, anche se qualcuno scrive male una maschera.

Il piano dei salvataggi

Un piano minimo, con SQL Server in modalità di recupero completo: una copia completa ogni notte, una copia differenziale ogni quattro ore durante il giorno lavorativo, una copia del registro delle transazioni ogni quindici minuti. Con questo piano, se qualcuno cancella per errore un mese di ordini alle 14:37, puoi ripristinare il database alle 14:36. Con il file Access sapevi tornare a ieri notte, se la copia era riuscita.

La copia che non è mai stata ripristinata non è una copia. Una volta al trimestre, ripristina l'ultimo salvataggio su un server di prova e apri il gestionale. Cinque minuti che separano l'azienda da una settimana di fermo.

Dove sta il server

Per un'azienda con una sede e uso interno, SQL Server Express su una macchina virtuale o su un server già presente basta quasi sempre: è gratuito e arriva a 10 GB per database. Per accessi da più sedi o da casa, Azure SQL Database elimina la gestione del server ma ha un canone mensile, che va verificato sul listino aggiornato di Microsoft perché cambia. In entrambi i casi la cifra va messa nel conto dei costi ricorrenti, non dimenticata.

Come si fa il passaggio senza fermare l'azienda?

Il passaggio si fa in un fine settimana, con una prova generale la settimana prima, un orario di blocco dei dati e un piano di ritorno scritto per tornare al vecchio file se qualcosa va storto. Si pianifica come un trasloco, non come un aggiornamento.

Le tre fasi

  1. Prova generale (una settimana prima). Copi il file di produzione, esegui l'intera migrazione su un server di prova, e fai lavorare due utenti esperti per una giornata, con i compiti reali. Cronometri ogni passaggio. L'obiettivo è scoprire gli errori mentre non costano nulla.
  2. Il blocco (venerdì sera). Tutti escono dal gestionale. Si fa l'ultima copia del file, si rinomina l'originale in sola lettura, si esegue la migrazione definitiva sulla copia congelata.
  3. Il collaudo (sabato). Si confrontano i conteggi, si aprono i flussi principali (offerta, ordine, fattura, ritiro), si distribuisce il nuovo front-end. Domenica sera il sistema è pronto, il lunedì si lavora.

Il piano di ritorno

Ogni passaggio serio ha un punto oltre il quale non si torna indietro, e prima di quel punto un modo semplice per tornare. Il file originale resta intatto in sola lettura per almeno un mese. Se il lunedì mattina si scopre un problema grave che non si risolve in un'ora, si riapre il vecchio file, si perdono le registrazioni del giorno (che si reinseriscono) e si riprova il weekend successivo. Il piano di ritorno scritto in anticipo toglie ansia a tutti, e quasi sempre non serve.

Il lunedì dopo

Per la prima settimana, qualcuno presidia: una persona che gira fra le scrivanie o che risponde subito al telefono. Gli utenti non sanno distinguere un errore di migrazione da un loro sbaglio, e chiamano per tutto. Prepara un elenco dei messaggi più probabili (permessi mancanti, driver non installato, stampante di default cambiata) con la cura per ciascuno. Alla fine della settimana, ripassa i registri del server e le dieci query più lente.

Quanto costa e quanto tempo richiede?

Per un file con una quarantina di tabelle e un centinaio di query, il lavoro richiede venti-venticinque giorni persona, circa 12.000-15.000 euro con la tariffa di riferimento di 600 euro al giorno. Il numero di tabelle conta meno del numero di query e di righe di codice VBA.

Tre casi con le ipotesi dichiarate

  • Piccolo: 15 tabelle, 25 query, 15 maschere, 4 utenti, 120 MB. Lavoro di 6-8 giorni, circa 4.000-5.000 euro. Il grosso è l'inventario e il collaudo.
  • Medio: 40 tabelle, 110 query, 60 maschere, 12 utenti, 1,7 GB, 4.000 righe di VBA. Lavoro di 20-25 giorni, circa 12.000-15.000 euro. È il caso dell'azienda di serramenti.
  • Grande: 120 tabelle, 400 query, 200 maschere, 30 utenti, più file collegati fra loro, 15.000 righe di VBA. Lavoro di 60-90 giorni, circa 36.000-54.000 euro, e a quel punto vale la pena di confrontarlo con la strada C.

Le cifre includono inventario, migrazione, correzione delle query lente, collaudo, passaggio e una settimana di presidio. Non includono l'hardware o il canone del server, né la formazione degli utenti se cambiano abitudini. Se qualcuno ti propone un prezzo fisso senza aver aperto il file, diffida: il costo sta dentro il file, e il file va aperto.

Che cosa fa salire il prezzo

Quattro cose, nell'ordine in cui le incontro. Il codice VBA che compone SQL concatenando stringhe, da riscrivere con parametri. I dati sporchi (duplicati, date impossibili, codici che non corrispondono) da ripulire prima del caricamento. Le integrazioni con altri file e altri programmi che il proprietario non ricorda. E la mancanza di qualcuno in azienda che conosca il processo e sappia dire "questo numero è giusto" nel collaudo. Quest'ultima voce non si compra, e senza di lei il tempo si raddoppia.

Che cosa fa scendere il prezzo

Un file ordinato, relazioni dichiarate, maschere costruite con le procedure standard, niente VBA che scrive SQL, una persona interna che dedica due ore al giorno al collaudo. Ho visto lavori da dodici giorni che ne sarebbero costati ventiquattro per l'assenza di un referente. Il costo di una migrazione è quasi tutto nell'organizzazione e poco nella tecnica.

Per decidere se la cifra ha senso, mettila a fianco del costo dell'inattività. Se il gestionale si ferma un giorno e dodici persone non possono lavorare, il conto di quella giornata (stipendi, ordini in ritardo, clienti che aspettano) è già superiore a una settimana di lavoro di migrazione. Ne ho parlato per esteso in quanto ti costa davvero il software legacy.

Quali errori si ripetono più spesso?

Gli errori più frequenti sono migrare senza inventario, lasciare i tipi di dato scelti dallo strumento, non aggiungere le chiavi e la colonna di versione, e non misurare le query dopo il passaggio. Quasi tutti si evitano con mezza giornata di preparazione.

  • Lanciare SSMA sul file di produzione. Lavorare sempre su una copia compattata e riparata, e conservare l'originale.
  • Accettare la mappatura automatica. Il testo da 255 caratteri per tutto, il vecchio datetime, il Sì/No che ammette nulli: ognuna di queste scelte ha un costo che si paga dopo.
  • Dimenticare le chiavi primarie. Tabelle che si leggono ma non si modificano, e nessuno capisce perché.
  • Ignorare gli indici. In Access il file piccolo nasconde le mancanze. Su SQL Server con milioni di righe, una colonna di filtro senza indice paga ogni ricerca.
  • Lasciare la password nel front-end. Un utente curioso apre il file e la legge. Autenticazione integrata, o password richiesta all'avvio.
  • Distribuire un solo front-end condiviso. È l'abitudine che porta alla corruzione del file che avevi appena risolto. Ogni utente ha la sua copia in locale, aggiornata da un piccolo programma all'avvio.
  • Non scrivere nulla. Dopo il passaggio, un foglio di due pagine con server, nome del database, utenze, piano dei salvataggi e il nome di chi sa. Quando la persona che l'ha fatto non c'è più, quella pagina vale più del codice.
  • Non misurare prima e dopo. Senza i tempi di partenza non puoi dire se la migrazione è riuscita. Cronometra le cinque operazioni più frequenti prima di cominciare.

Un caso reale, in breve

L'azienda di serramenti del Veneto ha fatto la strada B in ventidue giorni. Il file da 1,7 GB è diventato un database da 640 MB (Access conserva molto spazio inutilizzato e indici ridondanti). I dodici utenti hanno lavorato il lunedì con le stesse maschere. Tre report che prima richiedevano fra quaranta secondi e due minuti ora rispondono in meno di due secondi, dopo averli trasformati in viste. Le corruzioni del file sono finite. Il titolare ha speso circa 13.000 euro e ha comprato tempo: con i dati in ordine ha deciso con calma, un anno dopo, di scrivere in .NET il modulo preventivi, il più usato, lasciando le altre maschere in Access.

E dopo SQL Server, che cosa succede ad Access?

Dopo la migrazione dei dati, Access può restare come interfaccia per anni, oppure essere sostituito un modulo per volta con un programma web o desktop in .NET che usa lo stesso database. Il database su SQL Server è il pezzo che resta in tutti e due gli scenari.

Tenerlo, con criterio

Access non è scomparso e Microsoft continua a distribuirlo con Microsoft 365, ma sta in una zona di manutenzione più che di sviluppo: è sicuro per lavorare, meno per costruirci sopra novità. Se le maschere servono ancora a dodici persone e funzionano, tenerle è una scelta razionale. Basta sapere che lo stai facendo, e mettere nei conti la dipendenza da Windows, da Office e dalle versioni del driver.

Sostituirlo un pezzo alla volta

Se scegli C# per il nuovo modulo, qui trovi che cosa puoi fare con C# in azienda. E se i dati che ti servono non sono tabelle ma documenti da distribuire su più aree, esiste Azure Cosmos DB, il database NoSQL scalabile, che però è un'altra scelta e non un sostituto di SQL Server per un gestionale.

Il vantaggio di aver portato i dati su SQL Server è che un nuovo programma può usare le stesse tabelle mentre Access continua a funzionare. Scegli il modulo che dà più valore (spesso le offerte o la gestione degli ordini, quello che si usa da fuori ufficio), lo riscrivi con un accesso ai dati pulito, per esempio con Dapper o Entity Framework Core, e lo metti in produzione accanto alle maschere vecchie. Quando tutti usano il nuovo, la vecchia maschera si spegne. È l'approccio più sicuro per un software che non può fermarsi, e lo uso anche per i sistemi molto più vecchi, come quelli descritti in migrazione da VB6 a .NET.

La regola in cinque righe

  1. Se il file supera i 1,2 GB o si corrompe più volte l'anno, sposta i dati su SQL Server.
  2. Tieni le maschere Access finché funzionano, e misura cosa fanno prima e dopo.
  3. Prima di migrare, fai l'inventario: chiavi, relazioni, query con VBA, collegamenti esterni.
  4. Il passaggio è un fine settimana con una prova generale e un piano di ritorno scritto.
  5. Se hai oltre trenta utenti, più di 15.000 righe di VBA o dati che nessuno sa verificare, chiedi aiuto prima di cominciare.

Il titolare delle serramenta non ha comprato un gestionale nuovo e non ha buttato vent'anni di lavoro. Ha separato i dati dalle maschere, e ha fatto la cosa più importante: ha reso sicuro quello che vale di più, cioè lo storico degli ordini, e ha lasciato il resto da decidere con calma. Quasi sempre una migrazione ben fatta somiglia a questo, a un'operazione chirurgica più che a una rivoluzione.

Se hai un file Access che cresce, che si corrompe o che dipende da una persona sola, la mia proposta è semplice: mandami quattro numeri (utenti, dimensione del file, numero di query, minuti persi al giorno per utente) e in mezz'ora ti dico quale delle quattro strade conviene, quanto costa e qual è il primo passo. Se invece il problema è più largo, e riguarda un sistema da modernizzare per intero, trovi il metodo con cui lavoro nella pagina sulla modernizzazione del software legacy.

Domande frequenti

Per un file con una quarantina di tabelle e un centinaio di query servono circa 20-25 giorni persona, 12.000-15.000 euro con una tariffa di riferimento di 600 euro al giorno. Un file piccolo costa 4.000-5.000 euro. Il costo dipende dalle query e dal codice VBA più che dal numero di tabelle.

Sì. Le tabelle diventano collegate via ODBC al server e maschere, report e macro continuano a funzionare. Vanno corrette le query lente e il codice VBA che scarica tabelle intere.

Quasi sempre sì per le piccole aziende: è gratuito e arriva a 10 GB per database. Un file Access ha un tetto di 2 GB, quindi i dati ci stanno con ampio margine.

Succede di solito per una chiave primaria mancante, per un campo Sì/No che ammette valori nulli o per l'assenza di una colonna rowversion. Si risolve correggendo la definizione della tabella sul server.

Un fine settimana: prova generale la settimana prima, blocco dei dati il venerdì sera, collaudo il sabato, uso dal lunedì. Il file originale resta in sola lettura per un mese come piano di ritorno.

Per file ordinati sì. Per tabelle molto grandi, dati sporchi o trasformazioni di struttura conviene una migrazione scritta a mano con SqlBulkCopy e controlli sui conteggi.

Non per forza. Si può tenere finché funziona oppure sostituire un modulo per volta con un programma in .NET che usa lo stesso database.

Lascia i tuoi dati nel form qui sotto

Matteo Migliore

Matteo Migliore è un imprenditore e architetto software con oltre 27 anni di esperienza nello sviluppo di soluzioni basate su .NET e nell'evoluzione di architetture applicative per imprese e organizzazioni di alto profilo.

Nel corso della sua carriera ha collaborato con realtà come Cotonella, Il Sole 24 Ore, FIAT e NATO, guidando team nello sviluppo di piattaforme scalabili e modernizzando ecosistemi legacy complessi.

Ha formato centinaia di sviluppatori e affiancato aziende di ogni dimensione nel trasformare il software in un vantaggio competitivo, riducendo il debito tecnico e portando risultati concreti in tempi misurabili.

Stai leggendo perché vuoi smettere di rattoppare software fragile.Scopri il metodo per progettare sistemi che reggono nel tempo.