Use partial records on read operation
When a local record variable performs a read operation (Get, Find, FindFirst, FindLast, or FindSet) without a preceding SetLoadFields call, the runtime generates a SQL SELECT that includes every normal field in the table. On tables with many fields this transfers significantly more data from SQL Server than the procedure actually needs, increasing I/O, network traffic, and memory consumption.
The overhead compounds in loops. A FindSet / Next cycle over thousands of records fetches unused columns on every iteration — the cost scales linearly with both the field count and the result set size.
Calling SetLoadFields before a read tells the runtime to load only the fields you actually need, narrowing the SQL column list to the minimum. This is known as using partial records.
Example
codeunit 50100 MyCodeunit
{
procedure GetCustomerName(CustomerNo: Code[20]): Text
var
Customer: Record Customer;
begin
Customer.Get(CustomerNo); // Use partial records on read operation [PC0030]
exit(Customer.Name);
end;
}Add SetLoadFields before the read operation:
codeunit 50100 MyCodeunit
{
procedure GetCustomerName(CustomerNo: Code[20]): Text
var
Customer: Record Customer;
begin
Customer.SetLoadFields(Customer.Name);
Customer.Get(CustomerNo);
exit(Customer.Name);
end;
}Code fix
The ALCops: Add SetLoadFields code fix inserts a SetLoadFields call on the line before the read operation. It inspects the method body for field accesses on the variable and pre-populates the argument list with those fields, sorted alphabetically. When no field accesses are found, it falls back to the primary key fields.
FlowField, FlowFilter, and Blob fields are excluded because SetLoadFields does not support them.
For RecordRef variables the diagnostic is reported, but the code fix is not offered because field numbers are not statically determinable.
Exception
The diagnostic is not reported when:
- The variable is also used in a write operation (
Insert,Modify,Delete,Rename,TransferFields,Init,Copy) - The variable is passed as an argument to any function, event, or
PAGE.Run/PAGE.RunModal - The variable is a temporary record
- The variable is a global or parameter variable — only local variables are analyzed
Local variables only
The rule analyzes only local record variables. For a local variable the analyzer can see the full lifetime — declaration, every field access, and every read operation — within a single procedure body. That is enough to determine whether SetLoadFields is missing and which fields should be in the list.
Global and parameter variables are excluded because their usage spans multiple procedures, event subscribers, and page triggers. A static analyzer cannot reliably trace all code paths that access the variable, so it cannot guarantee that the inferred field list is complete. Reporting a diagnostic with an incomplete field list would cause JIT loads at runtime — the opposite of the intended improvement.
Data model for table extensions
The storage model for table extension fields has evolved across Business Central releases, changing the performance profile of SetLoadFields.
Before Business Central 2023 wave 2, each table extension created its own companion table in SQL Server. Reading from a table with N installed extensions required an (N+1)-way SQL join — one per extension. On heavily extended tables like Customer or Item, this could degrade read performance dramatically. SetLoadFields was critical here: by excluding extension fields from the load, entire joins could be eliminated.
From Business Central 2023 wave 2 (October 2023), the platform stores all extension fields for a given base table in a single companion table . Regardless of how many extensions are installed, at most one SQL join occurs.
From Business Central 2024 wave 1 (April 2024), table extensions defined in the same app as their target table have their fields and keys merged directly into the base table — no companion table, no join at all.
From Business Central 2026 wave 2 (October 2026), companion tables will be eliminated entirely, merging all extension fields into the base table.
Even without companion table joins, SetLoadFields remains valuable. Reducing the SQL column list cuts the volume of data transferred from SQL Server to the application server on every read. The gain grows with the number of fields on the table and the size of the result set — in looping scenarios over large tables, partial records can still cut read times significantly.
Relationship to AA0242
This rule complements the CodeCop rule AA0242 (“Field is not selected for loading and accessing it may cause a JIT load”). The two rules cover different scenarios:
| Rule | When it fires |
|---|---|
| PC0030 | SetLoadFields is entirely absent before a read operation |
| AA0242 | SetLoadFields is present but a field access is missing from the field list |
Together they provide full coverage: PC0030 encourages adopting partial records, and AA0242 ensures the field list stays complete.
See also
- Using Partial Records on Microsoft Learn
- Record.SetLoadFields on Microsoft Learn
- RecordRef.SetLoadFields on Microsoft Learn
- SetLoadFields best practices on ALGuidelines.dev
- Partial Records in detail (part 1) by Tomas Kapitan
- Partial Records in detail (part 2) by Tomas Kapitan
- SetLoadFields with reference and value passing by Stefano Demiliani
- Using partial records in Business Central by Ivan Singleton
- Partial records — Performance improvements by Volodymyr Dvernytskyi