diff --git a/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.bad.al b/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.bad.al index 0a70e85..1faf1b7 100644 --- a/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.bad.al +++ b/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.bad.al @@ -5,8 +5,8 @@ codeunit 50493 "Perf Record Clone Bad" Customer: Record Customer; CustomerCopy: Record Customer; begin - Customer.SetLoadFields("Credit Limit (LCY)"); Customer.SetFilter("Credit Limit (LCY)", '>0'); + Customer.SetLoadFields("Credit Limit (LCY)"); if Customer.FindSet(true) then repeat CustomerCopy.Copy(Customer); diff --git a/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.good.al b/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.good.al index 84bfda4..16e1160 100644 --- a/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.good.al +++ b/microsoft/knowledge/performance/avoid-cloning-records-before-modify-delete-in-loops.good.al @@ -4,8 +4,8 @@ codeunit 50492 "Perf Record Clone Good" var Customer: Record Customer; begin - Customer.SetLoadFields("Credit Limit (LCY)"); Customer.SetFilter("Credit Limit (LCY)", '>0'); + Customer.SetLoadFields("Credit Limit (LCY)"); if Customer.FindSet(true) then repeat Customer.Validate( diff --git a/microsoft/knowledge/performance/load-only-primary-key-fields-for-reference-work.good.al b/microsoft/knowledge/performance/load-only-primary-key-fields-for-reference-work.good.al index 5dcc499..7437a4b 100644 --- a/microsoft/knowledge/performance/load-only-primary-key-fields-for-reference-work.good.al +++ b/microsoft/knowledge/performance/load-only-primary-key-fields-for-reference-work.good.al @@ -6,8 +6,8 @@ codeunit 50100 "Item Reindex Queue" ReindexQueue: Codeunit "Reindex Queue"; begin // Only the primary key is used in the loop body; load nothing else. - Item.SetLoadFields("No."); Item.SetRange("Item Category Code", CategoryCode); + Item.SetLoadFields("No."); if Item.FindSet() then repeat diff --git a/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.bad.al b/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.bad.al index 86b1842..42779b4 100644 --- a/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.bad.al +++ b/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.bad.al @@ -4,8 +4,8 @@ codeunit 50491 "Perf AutoCalcFields Bad" var Customer: Record Customer; begin - Customer.SetLoadFields("Credit Limit (LCY)"); Customer.SetFilter("Credit Limit (LCY)", '>0'); + Customer.SetLoadFields("Credit Limit (LCY)"); if Customer.FindSet() then repeat Customer.CalcFields("Balance (LCY)"); diff --git a/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.good.al b/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.good.al index 072321c..3c1208f 100644 --- a/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.good.al +++ b/microsoft/knowledge/performance/use-setautocalcfields-for-per-row-flowfields.good.al @@ -4,8 +4,8 @@ codeunit 50490 "Perf AutoCalcFields Good" var Customer: Record Customer; begin - Customer.SetLoadFields("Credit Limit (LCY)"); Customer.SetFilter("Credit Limit (LCY)", '>0'); + Customer.SetLoadFields("Credit Limit (LCY)"); Customer.SetAutoCalcFields("Balance (LCY)"); if Customer.FindSet() then repeat diff --git a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.good.al b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.good.al index 772023c..a5bee5f 100644 --- a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.good.al +++ b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.good.al @@ -4,8 +4,8 @@ codeunit 50218 "Perf Sample LoadFields Good" var Customer: Record Customer; begin - Customer.SetLoadFields(Name); Customer.SetRange("Country/Region Code", 'US'); + Customer.SetLoadFields(Name); if Customer.FindSet() then repeat Message(Customer.Name); diff --git a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md index 85d20b3..1dcb8ba 100644 --- a/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md +++ b/microsoft/knowledge/performance/use-setloadfields-for-partial-records.md @@ -1,7 +1,7 @@ --- bc-version: [all] domain: performance -keywords: [setloadfields, partial-record, normal-field, flowfield, get, findset] +keywords: [setloadfields, partial-record, normal-field, flowfield, get, findset, statement-order] technologies: [al] countries: [w1] application-area: [all] @@ -11,11 +11,11 @@ application-area: [all] ## 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. +`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. 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`). +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`. @@ -23,4 +23,6 @@ See sample: `use-setloadfields-for-partial-records.good.al`. 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`.