bcquality/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md
Jesper Schulz-Wedde 53ecca3204 knowledge(performance): align SetLoadFields placement with AL Guidelines (#120)
The AL Guidelines mark `SetLoadFields` placed before `SetRange`/`SetFilter`
as bad code and recommend filters first, while the BCQuality samples used
the opposite order — contradictory guidance across two Microsoft repos.

Per Learn (`Record.SetLoadFields`), "fields that are filtered upon are
always loaded", so the two orders produce an identical projection. The
upstream rule is a readability convention: keep `SetLoadFields` adjacent
to the read it governs.

- Reorder filters ahead of `SetLoadFields` in the six affected AL samples.
- State the placement convention in the Best Practice section.
- Record in Description that order does not change the projection, and
  that only a fieldless `SetLoadFields()` or a later overwriting call does.
- Add an Anti Pattern note so review agents treat the reverse order as a
  readability observation, never a performance defect.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-17 13:11:17 +02:00

2.8 KiB

bc-version domain keywords technologies countries application-area
all
performance
setloadfields
partial-record
normal-field
flowfield
get
findset
statement-order
al
w1
all

Use SetLoadFields to load only the fields the code reads

Description

SetLoadFields(...) declares the subset of normal fields the next read should materialize, "reducing data read and transfer thereby improving performance significantly." Per the upstream guidance, "the gains scale with the amount of rows read, so for loops that read many rows SetLoadFields is even more important." Primary-key fields, SystemId, and system audit fields are loaded automatically, "and fields that are filtered on are also automatically included" — those do not need to appear in the list. SetLoadFields only affects FieldClass = Normal; it does not narrow FlowFields or FlowFilters. Its position relative to SetRange/SetFilter does not change the projection: filtered fields are added to the load set at read time either way. Only a fieldless SetLoadFields() — which resets the selection to all readable normal fields — or a later SetLoadFields(...) overwriting an earlier one changes what is loaded.

Best Practice

Before a Get, FindSet, or FindFirst that the procedure follows by reading only a handful of the table's fields, call SetLoadFields listing exactly those fields. The pattern SetLoadFields(...); if Record.Get(...) then ... is the upstream-endorsed shape. Place the call immediately before the read, after any SetRange/SetFilter, so a reader can see at a glance which read the selection governs and so no intervening statement can reset it. Skip SetLoadFields when the table has few fields (under ten), when the code reads most of them (above 60 %), when the loop runs ten or fewer iterations, or when the table is exempt for other reasons (singleton-setup-tables-need-no-access-optimization.md, temporary-tables-have-no-database-cost.md). For report dataitems, use AddLoadFields in OnPreDataItem instead (see addloadfields-in-report-onpredataitem.md).

See sample: use-setloadfields-for-partial-records.good.al.

Anti Pattern

Loading a wide table and reading one field per row in a loop. The bytes transferred per row are dominated by the columns the procedure does not touch; the SQL query selects them anyway. The same applies to a single Get on a wide table — the platform reads the whole row when a single field would have sufficed.

Statement order is not part of this anti pattern. SetLoadFields placed ahead of SetRange/SetFilter materializes exactly the same columns as the reverse order, so a reviewer reports it as a readability observation at most — never as a performance defect.

See sample: use-setloadfields-for-partial-records.bad.al.