← All postsDevelopment ยท 3 November 2026

Ten AL performance mistakes, and what to do instead

Business Central online is fast until someone writes a loop that reads the item ledger one record at a time. The ten performance mistakes we find most in per-tenant extensions, from missing SetLoadFields to locking the whole sales line table, each with the fix.

Most Business Central performance complaints are not the platform. They are an extension doing something expensive in a hot path: a posting routine, a page trigger, a report, an API call. These are the ten we find most often when a customer asks why month-end has got slower, in rough order of how much they cost.

1. Reading whole records when you need two fields

Customer.Get(No) pulls every field, including the blobs and the calculated ones you then CalcFields. Use SetLoadFields(Name, "Credit Limit") before the read so only those columns come back. In a loop over thousands of records it is the single biggest win available, and the newer base application does it everywhere.

2. Loops that query inside loops

For each sales line, Item.Get, then ItemLedgerEntry.SetRange, then CalcSums. That is three round trips per line; a 500-line order is 1,500 queries. Pull the items you need in one filtered read into a temporary table or a dictionary, pull the ledger sums with one CalcSums over a filter that covers all the items, then loop in memory.

3. Filtering on fields that are not in a key

A SetRange on a non-key field is a table scan. Check the keys on the table (and the SQL index they produce); if your filter is a common one, add a key in your extension, or better, a key that includes the fields you then read so SQL can satisfy the query from the index.

4. CalcSums and CalcFields without SIFT

FlowFields and CalcSums are fast when there is a SumIndexField on a key covering the filter; they are a full aggregate query when there is not. Define the SIFT key (SumIndexFields on a key with the filter fields) in your table or table extension, and use MaintainSIFTIndex deliberately.

5. Finding the last record with FindLast on an unsorted set

FindLast without a key that matches the sort order forces SQL to sort the whole result. Set the key first. Same for FindFirst when you only need existence: use IsEmpty, which asks SQL "is there one" rather than "give me one".

6. Locking the table when you meant to read it

LockTable or FindSet(true) early in a routine takes a lock that every other user then queues behind. Read with FindSet() (read-only), and take the update lock at the last moment, on the narrowest filter. Posting routines that lock Sales Line for the whole company while they loop are the usual cause of "the system froze at month-end".

7. Doing work in OnAfterGetRecord on a list page

Anything in OnAfterGetRecord runs for every row the page shows, every time it scrolls. A CalcFields or a lookup there turns a list into a crawl. Move it to a FlowField with a SIFT key, a query object, or a field maintained at write time.

8. Commits in loops, and commits at all

Commit inside a loop makes every iteration a transaction, which is slow and leaves the data half-written if the loop fails. Let the transaction scope do its job. If a long batch genuinely needs checkpoints, batch them in hundreds and know why.

9. Reports built on nested data items with no filters

A report with Customer, then Sales Invoice Header, then Sales Invoice Line as nested data items, with the filtering done in triggers, reads everything and discards most of it. Push the filters into the data item properties (DataItemLink, DataItemTableView) so SQL does the restriction, or use a query object as the source.

10. Not looking at the telemetry

Every tenant can stream telemetry to Application Insights: long-running SQL, locks, page load times, slow reports, by object and by user. It tells you exactly which codeunit and which query, with the AL call stack. Most businesses have never switched it on. Switch it on (one setting in the admin centre), and look at the "long running SQL queries" and "database locks" signals monthly. The performance work finds itself.

How to find yours

Switch on telemetry. Run the Performance Profiler on the slow process (it is built into the client) and read the call tree. Then apply the list above to the top three entries. In our experience the first afternoon of this on a slow system finds a 5x improvement on the worst process, and it is nearly always numbers 1, 2 and 6. The AL code check does exactly this on a fixed scope.

Talk to us
Want this done in your Business Central?
Book a 30 minute callGet the monthly newsletter

More from the blog