Partial Records in AL

Partial Records were indeed introduced in Business Central 2020 Release Wave 2 (version 17.0), and they represent a significant optimization feature for AL development.

What Are Partial Records?

Partial Records is a performance optimization feature that allows you to load only a subset of fields from a table instead of all fields when using the FindSet()FindFirst()FindLast(), and Get() methods.

Basic Syntax:

procedure ProcessCustomers()
var
    Customer: Record Customer;
begin
    // OLD WAY - loads ALL fields
    if Customer.FindSet() then
        repeat
            // Processes all fields
        until Customer.Next() = 0;

    // NEW WAY with Partial Records - loads only specified fields
    Customer.SetLoadFields(Customer."No.", Customer.Name, Customer.Blocked);
    if Customer.FindSet() then
        repeat
            // Only has access to "No.", Name, and Blocked fields
            if Customer.Blocked = Customer.Blocked::" " then
                Message('Customer %1: %2', Customer."No.", Customer.Name);
        until Customer.Next() = 0;
end;

Key Benefits and Use Cases

1. Performance Optimization

procedure GetCustomerNames()
var
    Customer: Record Customer;
begin
    // Only loads 2 fields instead of 100+ fields in the Customer table
    Customer.SetLoadFields(Customer."No.", Customer.Name);
    if Customer.FindSet() then
        repeat
            // Much faster database retrieval
            ProcessCustomerName(Customer.Name);
        until Customer.Next() = 0;
end;

2. Reduced Network Traffic

  • Especially important for SaaS environments
  • Minimizes data transfer between database and application server

3. Memory Efficiency

procedure CheckBlockedCustomers()
var
    Customer: Record Customer;
    Counter: Integer;
begin
    // Only load the essential fields for this specific task
    Customer.SetLoadFields(Customer.Blocked, Customer."No.");
    
    Customer.SetRange(Blocked, Customer.Blocked::All);
    if Customer.FindSet() then
        repeat
            Counter += 1;
        until Customer.Next() = 0;
        
    Message('Total blocked customers: %1', Counter);
end;

Important Rules and Limitations

Field Access Restrictions:

procedure ExampleWithLimitations()
var
    Customer: Record Customer;
begin
    Customer.SetLoadFields(Customer."No.", Customer.Name);
    
    if Customer.FindSet() then
        repeat
            // ✅ THIS WORKS - field is loaded
            Message(Customer.Name);
            
            // ❌ THIS CAUSES RUNTIME ERROR - field not loaded
            // Message(Customer."Phone No."); // ERROR!
            
        until Customer.Next() = 0;
end;

Multiple SetLoadFields Calls:

procedure MultipleLoadFields()
var
    Customer: Record Customer;
begin
    // Only the LAST call to SetLoadFields is effective
    Customer.SetLoadFields(Customer."No.");        // ❌ Ignored
    Customer.SetLoadFields(Customer.Name);         // ❌ Ignored  
    Customer.SetLoadFields(Customer.Blocked);      // ✅ This one is used
    
    if Customer.FindSet() then
        // Only Customer.Blocked field is loaded
end;

Combining Fields:

procedure CombineFields()
var
    Customer: Record Customer;
begin
    // Load multiple fields in one call
    Customer.SetLoadFields(Customer."No.", Customer.Name, Customer."Balance (LCY)");
    
    if Customer.FindSet() then
        repeat
            // All specified fields are accessible
            ProcessCustomerData(Customer."No.", Customer.Name, Customer."Balance (LCY)");
        until Customer.Next() = 0;
end;

Best Practices

1. Use in Loops with Limited Field Needs

procedure UpdateCustomerStatus()
var
    Customer: Record Customer;
begin
    // Only load fields needed for the status update
    Customer.SetLoadFields(Customer."No.", Customer.Blocked, Customer."Last Date Modified");
    
    if Customer.FindSet() then
        repeat
            if ShouldUpdateStatus(Customer) then
                UpdateCustomerStatus(Customer."No.");
        until Customer.Next() = 0;
end;

2. Avoid in Complex Business Logic

procedure ComplexProcessing()
var
    Customer: Record Customer;
begin
    // Don't use Partial Records if you might need various fields
    // Customer.SetLoadFields(...); // ❌ Don't limit fields here
    
    if Customer.FindSet() then
        repeat
            // This procedure might need different fields based on conditions
            if ConditionA then
                UseFieldA(Customer);
            else if ConditionB then
                UseFieldB(Customer);
                
        until Customer.Next() = 0;
end;

3. Ideal for Reporting and Statistics

procedure CalculateCustomerStatistics()
var
    Customer: Record Customer;
    TotalBalance: Decimal;
begin
    // Perfect use case - only need specific fields for calculations
    Customer.SetLoadFields(Customer."Balance (LCY)", Customer."Customer Posting Group");
    
    if Customer.FindSet() then
        repeat
            TotalBalance += Customer."Balance (LCY)";
        until Customer.Next() = 0;
        
    Message('Total customer balance: %1', TotalBalance);
end;

Performance Impact Example

Consider a Customer table with 150 fields:

Without Partial Records:

  • Loads 150 fields × 10,000 records = 1,500,000 field values
  • Significant memory and network usage

With Partial Records (loading 3 fields):

  • Loads 3 fields × 10,000 records = 30,000 field values
  • 98% reduction in data transfer!

Summary

Partial Records, introduced in BC v17, provide:

  • ✅ Major performance improvements for data retrieval
  • ✅ Reduced memory consumption
  • ✅ Minimized network traffic (crucial for cloud)
  • ✅ Cleaner code intent – explicitly declaring data needs

Important Considerations:

  • ❌ Runtime errors if accessing non-loaded fields
  • ❌ Only the last SetLoadFields() call is effective
  • ❌ Not suitable for all scenarios (be strategic)

This feature represents Microsoft’s ongoing commitment to optimizing Business Central performance, especially important as systems grow larger and more complex in enterprise environments.


Sources

Leave a Reply

Your email address will not be published. Required fields are marked *