What is update_recordset
in X++?
In Microsoft Dynamics 365 Finance & Operations (and earlier Dynamics AX versions), update_recordset
is a set-based X++ statement used to update multiple records in a single operation. For a more general intro to the x++ programming language syntax IDE and more, you may want to check our what is x++ introduction guide first so you could a general idea what the x++ programming language even is and what it is usually used for in relation to both the general ideas of postfix versus prefix incrementation but more importantly the specifics about Microsoft Dynamics 365 Finance and Operations X++ Development using the programming language which is actually named X++ (pronounced X Plus Plus).

So when it comes to x++ update_recordset vs update, well instead of fetching and updating each record one-by-one in a loop, an update_recordset
translates to just a single SQL UPDATE
query for you that operates on all records meeting the given criteria. This means it sends one request to the database to update all targeted rows at once, leveraging the SQL server’s capabilities for better performance. In essence, update_recordset
in X++ is analogous to a SQL UPDATE ... SET ... WHERE ...
statement executed on the database server side.
Because it is set-based, update_recordset
can modify multiple rows and even multiple columns in one go. You specify the table to update, the field assignments (new values or expressions for one or more fields), and an optional where
clause to filter which records should be updated. You can even use joins in an update_recordset
to base the update on related data from other tables – a capability that is not available when updating records one by one using the standard method. For example, an official Microsoft example shows joining three tables in an update_recordset
to set an employee status field based on data from Department and Project tables.
In summary, update_recordset
is a powerful X++ construct for bulk updates: it allows you to update a set of records (potentially many rows) with new values in one database trip, rather than iterating through records in X++ code. This drastically reduces the communication between the application server and the database, which improves performance for large data operations.
How update_recordset
Differs from the update()
Method
The traditional update()
method in X++ (called on a table buffer) updates one record at a time – specifically, the current record held in that buffer. In practice, using update()
to modify multiple records means you must retrieve each record (e.g. in a loop or via separate selects) and call record.update()
for each one. Each of those calls results in a database operation (or at least a round trip when the transaction is flushed), and X++ will handle them one-by-one. This row-by-row approach is straightforward but can be inefficient for large numbers of records, because it involves repeated context switching and multiple database calls. The example below illustrates a row-by-row update using the update()
method:
// Example: Increase credit limit by 1000 for each customer (row-by-row)
CustTable cust;
ttsBegin;
while select forUpdate cust
where cust.CreditLimit < 5000
{
cust.CreditLimit += 1000;
cust.update(); // updates one record at a time
}
ttsCommit;
In contrast, update_recordset
performs a bulk update in one operation. There is no explicit loop in your X++ code; instead, the X++ runtime translates the statement into a single SQL update query that the database executes on all matching records. This set-based approach is much faster for large data sets, since it avoids the overhead of sending many individual update commands to the database. Using the same scenario as above, an update_recordset
can achieve the update in one go:
// Example: Increase credit limit by 1000 for all customers with CreditLimit < 5000 (set-based)
CustTable cust;
ttsBegin;
update_recordset cust
setting CreditLimit = cust.CreditLimit + 1000
where cust.CreditLimit < 5000;
ttsCommit;
In this single statement, every customer record with CreditLimit under 5000 gets updated, all in one database trip. This highlights the core difference: update()
updates the current record (one at a time), whereas update_recordset
updates all records meeting the condition in one operation.
Another key difference involves business logic and system fields updates. The update()
method may have overridden logic in the table’s code (for example, validations or custom computations in the table’s update()
method or event handlers like onUpdating
/onUpdated
). When you call cust.update()
, all such logic will execute for that record. With update_recordset
, by default, X++ will skip calling per-record business logic on each row to gain performance – it directly issues an SQL update. If the table’s update()
method is overridden with custom code, the runtime will actually fall back to record-by-record updates in order to respect that logic (essentially treating the update_recordset
as if it were a loop calling update()
on each record). In other words, certain conditions (like an overridden update method, database logging enabled on the table, alerts, etc.) will cause the set-based operation to downgrade to row-by-row mode to ensure no business rules are bypassed. This protects data integrity but also means you lose the performance benefit in those cases.
It is possible to explicitly override this safety fallback if you are sure it’s safe. X++ provides methods such as skipDataMethods()
, skipDatabaseLog()
, skipEvents()
, etc., which you can call on the table buffer before an update_recordset
to force skipping of custom update method logic, database logging, or event handlers respectively. For example, calling cust.skipDataMethods(true)
before the update_recordset
will prevent the system from invoking the overridden update()
method on each record, ensuring the operation stays set-based. Use these with caution – they should only be used when you intentionally want to bypass those triggers or validations for performance reasons. In general, if you need the custom logic to run for each record, you might stick to using the standard update()
in a loop; if not, update_recordset
gives better speed.
To summarize the differences:
- Scope of Update: The
update()
method operates on one record at a time (the record in the buffer), whereasupdate_recordset
can update multiple records at once matching a query condition. - Performance:
update_recordset
is set-based and uses a single round-trip to the database for all records, making it significantly faster for bulk updates. Theupdate()
method involves multiple round-trips (one per record or per small batch) which is slower for large volumes. - Business Logic: The
update()
method will execute any overridden table logic and fire events for each record.update_recordset
will not individually execute record-level logic by default, unless it has to fall back to row-by-row mode because such overrides or logging are present. - Syntax and Usage:
update_recordset
uses a declarative SQL-like syntax (setting Field = expression where condition
), and can update several fields and even use joins in one statement. Theupdate()
method is an imperative call in X++ that requires you to handle record selection and looping explicitly in your code.
What is update_recordset
Used For?
The primary use case for update_recordset
is performing bulk updates efficiently. Whenever you need to update many records in a table – for example, applying a uniform change or formula to a large set of rows – update_recordset
is typically the best tool. By updating multiple records in one database trip, it reduces network/database overhead and improves performance for mass updates. In scenarios such as data migration, periodic maintenance jobs, or large-scale adjustments to data (e.g. end-of-month routines, mass price updates), using update_recordset
can make the operation complete much faster than looping with individual updates.
Typical situations where update_recordset
is used include:
- Mass attribute changes: e.g. setting a new status or default value on all records that meet certain criteria. Rather than iterating through each record, a single
update_recordset
can flip a status flag on thousands of records at once. - Bulk corrections or data fixes: e.g. increasing all inventory item prices by 10% for a category of products, or adjusting a numeric field across many records due to a business policy change.
- Periodic batch jobs: In batch processes where lots of records need updates (for instance, applying interest to all savings accounts, or updating the next billing date for all active contracts),
update_recordset
helps achieve this in a set-based, efficient manner. - Improving legacy code performance: If an older code pattern is doing record-by-record updates in a loop and hitting performance limits, it can often be refactored to a single
update_recordset
statement (assuming the logic per record is simple enough to express in a set-based way). This can drastically shorten execution time. - Updating multiple fields together: You can set multiple columns in one
update_recordset
(using thesetting Field1 = ..., Field2 = ...
syntax), which is useful when a change involves multiple related fields. This not only saves additional trips but also ensures the fields are updated in one atomic operation for consistency.
One thing to note is that update_recordset
works best when the update you need to perform is relatively uniform for all records (e.g. a formula or a fixed new value that can be calculated in SQL). If each record needs unique complex logic to determine the new value that cannot be expressed in a single SQL expression, you may not be able to use update_recordset
directly and might resort to looping with update()
. However, many common updates (adding a constant, multiplying by a factor, setting a specific enum value, etc.) are perfectly suited to the set-based approach.
In summary, update_recordset
is used whenever you want to update many rows efficiently in Dynamics 365/AX and there is no need to execute custom business logic for each individual record (or you have explicitly chosen to skip that logic for performance). It’s a way to let the database do the heavy lifting in bulk, which is aligned with writing high-performance X++ code.
Examples of update_recordset
in Action
To illustrate how update_recordset
can be applied, let’s look at both a standard example and a few realistic scenarios in different industries. All examples assume the relevant tables and fields already exist in the Dynamics 365 F&O data model, and we focus only on the X++ update logic (within a transaction).
Standard Example – Increase Customer Credit Limits: A classic example (from Microsoft’s documentation) is increasing the credit limit for all customers who have a positive current limit. Using update_recordset
, this can be done in one query, as shown below:
CustTable cust;
ttsBegin;
update_recordset cust
setting CreditMax = cust.CreditMax + 1000
where cust.CreditMax > 0;
ttsCommit;
In this example, every customer’s CreditMax field is incremented by 1000 if their current CreditMax is greater than 0. All qualifying records in CustTable
are updated at once by the single statement inside the transaction. This contrasts with how one might have to do it with a loop and individual cust.update()
calls without update_recordset
. Similarly, you could update multiple columns in one go or base the update on a join (as discussed earlier), all within the same update_recordset
statement.
Below are some real-world scenario examples in different business contexts, demonstrating how one might use update_recordset
to perform bulk updates efficiently:
1. Electronics Manufacturer Scenario
Scenario: An electronics manufacturing company needs to increase the base price of all products in the “Accessories” category by 10%. Instead of updating each product one by one, they can use update_recordset
to apply this price hike to all relevant products in one operation.
ProductTable prod;
ttsBegin;
update_recordset prod
setting BasePrice = prod.BasePrice * 1.10
where prod.Category == "Accessories" && prod.Active == NoYes::Yes;
ttsCommit;
Explanation: This X++ code finds all products in the ProductTable
where the Category is “Accessories” and the product is marked as active, and increases each product’s BasePrice by 10%. The condition ensures inactive or other-category products are untouched. All matching product records are updated together within the single transaction. This is ideal for a mass price update scenario – it executes quickly and ensures consistency (all prices are updated to the new value range at the same time).
2. Real Estate Developer Scenario
Scenario: A real estate development firm wants to mark multiple properties as “Completed” in their system when construction has finished. For example, when a batch of units in a housing project reach their completion date, the status of those properties should be updated from “UnderConstruction” to “Completed”. Using update_recordset
, this can be done for all finished properties at once.
PropertyTable property;
ttsBegin;
update_recordset property
setting Status = PropertyStatus::Completed
where property.Status == PropertyStatus::UnderConstruction
&& property.CompletionDate <= today();
ttsCommit;
Explanation: This code updates the PropertyTable
by setting the Status field to “Completed” for every property record that is currently labeled “UnderConstruction” and whose CompletionDate
is today or in the past (meaning the construction is done). The use of update_recordset
lets the developer update all such properties in one go, for example at the end of the day when several construction projects might complete. Without set-based updates, they might have to loop through each property or rely on manual updates, which is error-prone and slower.
3. Construction Company Scenario
Scenario: A construction company needs to adjust the budgets of all ongoing projects due to rising material costs. Suppose they decide to increase the budget by 5% for all projects that are still active and of type “Infrastructure”. Instead of updating each project individually, an update_recordset
can handle it collectively.
ProjectTable proj;
ttsBegin;
update_recordset proj
setting Budget = proj.Budget * 1.05
where proj.Status == ProjectStatus::Active
&& proj.ProjectType == ProjectType::Infrastructure;
ttsCommit;
Explanation: Here, every project in ProjectTable
that has an Active status and is an Infrastructure project will get its Budget field multiplied by 1.05 (a 5% increase). This single statement applies the change to all qualifying project records. The transaction ensures that either all budgets are updated or none are if something goes wrong, maintaining data consistency. This kind of bulk update is useful for company-wide budget revisions and is much faster than iterating through each project record with a separate update.
4. International Investment Firm Scenario
Scenario: An international financial management firm wants to standardize interest rates for a certain type of account across all regions. For instance, they decide that all “Savings” accounts globally should have their interest rate set to 5%, overriding any previous rate differences. Using update_recordset
, they can implement this policy change in one shot.
Account account;
ttsBegin;
update_recordset account
setting InterestRate = 0.05
where account.Type == AccountType::Savings;
ttsCommit;
Explanation: This example updates the Account
table, setting the InterestRate field to 0.05 (5%) for every account that is of type “Savings”. In one query, all savings accounts in the system are updated to the new standard rate. This demonstrates the power of update_recordset
for a financial scenario – a broad business rule change (like aligning interest rates) can be applied instantly to all affected records. There is no need to loop over accounts or handle them per region; the set-based update handles it globally in the context of the database. If some accounts had custom logic on update (for example, auditing changes), one would need to ensure that is handled appropriately (either allowed to trigger per record via fallback or consciously skipped if not needed).
Each of the above scenarios shows how update_recordset
can simplify and accelerate what would otherwise be a repetitive update task. By using a set-based approach, the code is concise and clear in intent – it describes what change to make to which set of records, and leaves the heavy lifting to the database engine. This results in performance gains and more maintainable code for bulk updates. Just be mindful of the caveats: if there are complex business rules on the table, you may need to handle those (or let X++ revert to row-by-row mode), and always wrap such operations in ttsBegin/ttsCommit
to maintain transactional integrity, as shown in the examples. Overall, understanding when to use update_recordset
versus the standard update()
method is an important part of writing efficient X++ code in Dynamics 365 Finance & Operations.
Have a Question ?
Fill out this short form, one of our Experts will contact you soon.
Call Us Today For Your Free Consultation
Call Now