while select statement or the QueryRun class (which also uses the same loop construct). Looping through rows of data and performing an operation on each row is very common in F&O and is also known as "row-by-row" operations.
Row-by-row operations are very flexible because, for each row, you can perform custom logic on the data. For example, you can loop through all customers and check their current balance, or loop through all customers and check their invoice aging to see if they have any outstanding amounts older than 60 days.
However, row-by-row operations handle each record separately and apply business logic to every single row. This adds significant overhead, which can make processing large datasets painfully slow. Performance also depends heavily on the complexity of the business logic. Complex logic often involves multiple calls to other methods or business logic layers, which can quickly create deep nested calls.
Set based operations
So, how can we handle large datasets and get good performance? With set based operations like insert_recordset, update_recordset and delete_recordset.
Set-based operations are executed with a single SQL statement and a single round trip to the SQL Server database. This is why they are so fast. Even if millions of records are affected, only one SQL statement is sent to the backend SQL Server database.
Falling back to row-by-row operations
The set based operation will fall back to a row-by-row operation in the following cases if:
- One of the data methods has been overriden on the target table:
insert()update()delete()
- The database log in enabled on the target table
- There is a delete action on the target table and you are using
delete_from - There is an alert setup on the target table
- The ValidTimeStateFieldType table property not equal to None
For example, let's say you are inserting data into a table using the insert_recordset statement, but the insert() method of the target table contains business logic. In this case, the system will automatically fall back to row-by-row operations. This means each inserted row will result in a separate SQL statement and round trip to the database server. As a result, there is significant performance overhead, and you gain little to no performance benefit compared to a regular while select loop.
Forcing set based operations
If for one or more of the reaons above, the set based operation is falling back to a row-by-row operation and you still want to force a set-based operation. You can use the .skip... methods on the target table buffer.
For example:.skipDataMethods(true) can be used if one of the insert(), update() or delete() methods have been overridden.
See the table below on how to handle each case. Although it was originally written for AX 2012, it remains fully relevant for Dynamics 365 Finance and Operations (F&O).
| DELETE_FROM | UPDATE_RECORDSET | INSERT_RECORDSET | ARRAY_INSERT | Use ... to override | |
| Non-SQL tables | Yes | Yes | Yes | Yes | Not applicable |
| Delete actions | Yes | No | No | No | skipDeleteActions |
| Database log enabled | Yes | Yes | Yes | No | skipDatabaseLog |
| Overridden method | Yes | Yes | Yes | Yes | skipDataMethods |
| Alerts set up for table | Yes | Yes | Yes | No | skipEvents |
| ValidTimeStateFieldType
table property not equal to None |
Yes | Yes | Yes | Yes | Not applicable |
Only the table above sourced from: Microsoft Learn - Maintain Fast SQL Operations
Conclusion
Set based operations are excellent for performance on large datasets but fall back to row-by-row operations if the target table insert, update and delete methods have been overridden. Or for one of the other reasons as mentioned above.
Important: carefully consider if you can skip any data methods on the target table because you risk breaking the data integrity of the target table.