Quick answer
An MS Access database slows down for a handful of predictable reasons: missing indexes on filtered or joined fields, queries or forms pulling full tables instead of a filtered subset, an unsplit database under multi-user load, or a back-end that has not been compacted in a long time. Fix indexing and query filtering first — most 5-10 second form loads drop to under a second without touching the underlying business logic.
Symptom, cause, and fastest fix
Match what you are seeing to the row below before you touch anything — it saves you from indexing the wrong field or splitting a database that was never the problem.
| Symptom | Likely cause | Fastest fix |
|---|---|---|
| Forms or reports take 5+ seconds to open | Unindexed field in the WHERE/JOIN, or the form is bound to a full table | Index the filtered field; move the filter into RecordSource SQL |
| Fine alone, crawls with 5+ people logged in | Unsplit database, or the back-end sits on a weak/wireless network path | Split front-end/back-end; put the back-end on a wired connection |
| Gets slower every month even though data barely changed | File bloat from no regular compact & repair | Schedule compact & repair on the back-end |
| One report or import freezes the whole database | Nested subqueries or SELECT * pulling entire tables | Filter early, project only needed columns, break up nested queries |
| A specific lookup (customer, part number) is painfully slow | No index on that searched field | Add a single index on that field |
| File is near or over 1GB and still growing | Approaching the 2GB .accdb ceiling | Archive old records; plan a SQL Server back-end |
Why an Access database becomes slow
Jet/ACE executes the SQL you give it faithfully. When the plan behind that SQL is bad, you pay for it in seconds per interaction, not milliseconds. I have watched teams replace workstations and add RAM while a single CRM or inventory form kept pulling an unfiltered dynaset across 80,000-120,000 rows on every tab click — the hardware was never the bottleneck.
- Unindexed fields in filters and joins — a frequent WHERE or JOIN on a non-indexed column forces a full table scan. On 50k-100k rows, that is the difference between a sub-second seek and a 6-15 second form open.
- Heavy queries across large tables — nested subqueries and nonselective joins multiply work, and one bad saved query reused by five forms spreads the pain everywhere.
- Full-table-bound forms and reports — a continuous form or combo box bound to an unfiltered SELECT * drags the entire table to the client before you ever sort or filter in the UI.
- Network latency on a split back-end — chatty SQL over a weak Wi-Fi path looks like “network slowness” but is still a design problem underneath it.
- File bloat — deleted rows and temp growth leave dead space in an .accdb that has never been compacted, slowing I/O and backups alike.
For the full breakdown of why large datasets specifically expose these issues, see why Access slows down with large data.
Step 1 — Diagnose before you touch anything
You cannot fix what you have not measured. Guessing leads to random indexing or an unnecessary SQL Server migration that leaves the real bottleneck in place. Rank the slowest forms and reports by user complaints and observed wait time, then check what row count each one actually pulls versus what the user needs to see on screen.
A simple VBA timer around the suspect query tells you exactly where the time is going, without guessing from the UI:
Public Sub TimeQueryPerformance(queryName As String)
Dim startTime As Single
Dim db As DAO.Database
Dim rs As DAO.Recordset
startTime = Timer
Set db = CurrentDb
Set rs = db.OpenRecordset(queryName, dbOpenSnapshot)
rs.MoveLast ' forces the full recordset to actually populate
Debug.Print queryName & ": " & Format(Timer - startTime, "0.000") & _
" sec, " & rs.RecordCount & " rows"
rs.Close
Set rs = Nothing
Set db = Nothing
End SubReal example: A job-costing form bound to a query returning 100,000 detail lines opened in 12 seconds. Replacing it with a filtered key set (current job only) dropped open time under one second without changing a single business rule.
Steps 2-5 — Fix it in the right order
Step 2 — Index the fields your queries actually use
Add indexes on fields used in JOINs, WHERE clauses, and ORDER BY — especially foreign keys. Do not index everything; every index slows inserts and updates, so keep the set tied to real query patterns.
Real example: A customer search subform filtered on LastName with no index averaged roughly 8 seconds. A single index brought a typical lookup to about half a second on a 60,000-row table.
Step 3 — Fix the queries behind your slowest forms
Most Access slowness traces back to a handful of saved queries multiplied across the UI: avoid SELECT * in stacked queries, filter as early as possible in the SQL, use the correct JOIN type, and break monster nested queries into staged steps or temp tables where it removes repeated work.
This is the deepest, most technical part of the fix — for query-by-query rewrites and execution-plan detail, see our dedicated guide to fixing slow Access queries and query performance troubleshooting.
Step 4 — Trim what forms and reports actually load
Put real WHERE conditions in RecordSource SQL instead of loading everything and filtering in code. Load detail subforms on demand after the parent record is chosen, and avoid continuous forms bound to huge recordsets — use key-driven navigation instead.
Step 5 — Reduce contention with a proper split
Split the database — local or deployed front-ends against a shared back-end on a reliable wired path — and stagger heavy batch jobs away from peak hours. Splitting alone will not fix an unindexed query, but it removes the file-locking and contention layer sitting on top of it.
Do not skip compact & repair
File bloat is the quiet performance killer nobody notices until the back-end is 400MB of mostly dead space. A programmatic compact, run on the back-end from a small utility database, reclaims that space:
Public Sub CompactDatabase()
Dim strSource As String
Dim strTemp As String
strSource = CurrentDb.Name
strTemp = Left(strSource, Len(strSource) - 6) & "_temp.accdb"
DBEngine.CompactDatabase strSource, strTemp
Kill strSource
Name strTemp As strSource
MsgBox "Database Compacted Successfully"
End SubCompact requires exclusive access, so this only runs cleanly with every user out of the file. For the scheduling approach, permission errors, and the “file in use” workaround, see the full compact and repair guide.
Real business case: distribution inventory
Scenario: roughly 120,000 line records, 10 concurrent users on a split database, file size around 250MB before cleanup.
Before: stock lookup and receiving forms took 8-12 seconds to open; users force-quit weekly; month-end reporting froze other sessions.
Intervention: indexed part number and location keys, rewrote the slowest join queries, moved long-running reports to pre-aggregated tables, scheduled back-end compact, and tightened form record sources to the active warehouse only.
After: interactive screens under one second in normal use, stable multi-user behavior, and predictable overnight batch windows instead of random daytime lockups.
When it is not actually a performance problem
Slow and unstable are not the same issue. If the database is also throwing “unrecognized database format” errors, forms will not open at all, or it crashes outright rather than just running slow, that points to corruption or a size-related crash rather than a pure performance gap:
- Database crashes or will not open — see fixing an Access database that keeps crashing.
- File is corrupted or shows “unrecognized format” — see Access corruption repair or Access data recovery.
- File is approaching the 2GB ceiling — see Access database size issues.
Common mistakes we see in the field
- No indexing on the fields every query actually filters on.
- Excel-style flat sheets imported as one giant table with repeated headers and no keys.
- Loading entire tables into forms “because it used to be fast when we had 2,000 rows.”
- Refusing to split front-end and back-end, so every schema tweak becomes a fight.
- Ignoring compact & repair for years while imports and deletes quietly bloat the file.
When optimization is not enough
If your Access database is past 300-500MB with multiple active users and still struggles after indexing, query fixes, and compacting, the problem is structural, not temporary. At that point you need a deliberate plan — often upsizing the data layer to SQL Server while keeping Access as the front-end, rather than more tuning.
For that migration path, see Access-to-SQL Server migration. If you want us to run the full diagnosis and fix end to end rather than doing it yourself, that is exactly what our Access performance optimization service covers.
Frequently asked questions
Why is my Access database suddenly slow?
It rarely happens overnight — a database usually crosses a threshold that exposes a design gap that was always there: more rows past an unindexed field, more concurrent users on a shared back-end, or a file that finally passed the point where bloat matters. Check row counts, user count, and file size first; a jump from 1-2 seconds to 10 seconds almost always traces back to one of those three growing past what the current design tolerates.
What are the most common causes of a slow MS Access database?
Missing indexes on fields used in WHERE and JOIN clauses, queries that pull full tables instead of filtered subsets, forms bound to entire tables, an unsplit database with multiple users hitting one file, and a back-end that has never been compacted. On tables past 50,000-100,000 rows, a single missing index can turn a sub-second lookup into a 10+ second wait.
How do I know if my Access database needs indexing?
Index any field that shows up repeatedly in JOINs, WHERE clauses, or ORDER BY — foreign keys especially. If a filter or lookup on a large table takes several seconds while the same operation on a small table is instant, a missing index is almost always the reason. Do not over-index, though: every index adds overhead to inserts and updates, so add them only where a real query pattern needs them.
Can I compact an Access database while users are connected?
No — compacting requires exclusive access to the file, so every user has to be out first. On a live system, the usual workaround is to copy the back-end, compact the copy, then swap it in during a maintenance window. The account running the compact also needs create, delete, and rename rights on the folder, which is the other common failure point alongside "file in use."
Does splitting the front-end and back-end fix performance problems?
Splitting is necessary for multi-user stability, but it does not fix unindexed filters, full-table-bound forms, or bloated queries by itself. It removes contention and file-locking issues; you still need proper indexing, filtered RecordSource SQL, and a compact schedule to get the actual speed gains.
How many users can an Access database handle before it slows down?
Most Access consultants, including Microsoft’s own guidance, treat 10-25 concurrent users as the practical range for a split database on a stable wired network. Past that — especially with heavy simultaneous writes — you will typically see contention regardless of how well the queries and indexes are tuned, and it is time to consider a server-based back end like SQL Server.
What file size makes an Access database slow?
There is no hard cutoff, but performance issues tend to show up as an .accdb approaches a few hundred megabytes without regular compacting, and the format hard-caps at 2GB total. A file nearing that ceiling with active daily growth is a strong signal to archive old data or move to a different back end before you hit the wall.
Is it true that Access cannot handle large datasets?
Mostly a myth. The Jet/ACE engine can handle millions of rows if the tables are indexed and the queries are written to filter early — the real constraints are the 2GB file-size cap and concurrent write load, not raw row count. Most "Access is slow with big data" complaints trace back to missing indexes or full-table queries, not the engine’s actual limits.
When should I move from optimizing Access to migrating to SQL Server?
Once you have sustained write-heavy usage, real HA/DR or compliance requirements, or a database past roughly 300-500MB with multiple users that is still struggling after indexing, query fixes, and compacting, optimization alone stops being enough. At that point the usual path is upsizing the data layer to SQL Server while keeping Access as the front-end.
Will fixing slow performance also fix crashes or corruption?
No — slowness and instability are different problems with different causes, even though a badly bloated file can contribute to both. If you are also seeing "unrecognized database format" errors, forms that will not open, or the database crashing outright, that is a corruption or crash issue, not a pure performance one, and needs its own diagnosis.
Related resources
- Fixing slow Access queries — query-level rewrites and execution plans.
- Access performance optimization (service) — a dedicated engagement for tuning and remediation.
- Access-to-SQL Server migration — when optimization alone stops being enough.
- VBA automation — macros, integration, and scheduled maintenance.
Still dealing with a slow or freezing Access database?
We fix performance issues in real business systems handling large datasets and multiple users — not slide decks, not generic checklists.