insert_recordset, update_recordset, delete_from : performances X++
Les opérations ensemblistes X++ traitent des milliers de lignes en un aller-retour SQL. Syntaxe, exemples Learn et cas où elles repassent ligne par ligne.
La réponse courte
En X++, insert_recordset, update_recordset et delete_from agissent sur un ensemble d'enregistrements en un seul aller-retour vers la base, au lieu d'une boucle while select qui appelle insert(), update() ou delete() pour chaque ligne. Sur des volumes importants, c'est souvent le changement de code qui apporte le plus de gain de performance.
Mais ces instructions repassent automatiquement en traitement ligne par ligne dans plusieurs cas : méthode insert/update/delete surchargée sur la table, journal de base de données activé, alertes configurées, actions de suppression, tables non SQL ou tables à validité temporelle. Les méthodes skipDataMethods, skipDatabaseLog, skipEvents et skipDeleteActions permettent d'ignorer certains de ces facteurs, à utiliser en connaissance de cause.
insert_recordset : copier en une instruction
insert_recordset copie des données d'une ou plusieurs tables sources vers une table cible en un seul aller-retour. La liste de champs cible doit correspondre, dans l'ordre, à la liste de champs sélectionnés ; les champs absents reçoivent 0 ou vide, et RecId est attribué par le noyau. Exemple de Microsoft Learn, qui agrège par nom :
ValueSumByName valueSumName;
NameValuePair nameValuePair;
insert_recordset valueSumName (Name, ValueSum)
select Name, sum(Value)
from nameValuePair
group by Name;On peut aussi joindre plusieurs tables et insérer des valeurs de variables (avec firstonly pour une seule ligne). Les littéraux, comme 128 ou "texte", ne sont pas acceptés comme source. Si vous devez transformer chaque ligne avant insertion, RecordInsertList ou RecordSortedList (insertDatabase) regroupent aussi les insertions en un appel, avec plus de souplesse mais moins de vitesse qu'insert_recordset.
update_recordset et delete_from
update_recordset modifie plusieurs lignes en une instruction, à la manière d'un UPDATE … SET en SQL, et accepte des jointures pour alimenter les valeurs. Exemple Learn :
CustTable custTable;
ttsBegin;
update_recordset custTable
setting CreditMax = custTable.CreditMax + 1000
where custTable.CreditMax > 0;
ttsCommit;delete_from supprime toutes les lignes qui répondent à la condition, avec possibilité d'exists join ou notexists join. Le contraste avec une boucle :
// À éviter : un appel SQL par ligne
ttsBegin;
while select forUpdate tabWidget
where tabWidget.quantity <= 100
{
tabWidget.delete();
}
ttsCommit;
// Préférez : une seule instruction
ttsBegin;
delete_from tabWidget
where tabWidget.quantity <= 100;
ttsCommit;Quand l'opération repasse-t-elle ligne par ligne ?
Selon Microsoft Learn, la conversion en traitement ligne par ligne a lieu dans ces cas :
- tables non SQL (par exemple tables temporaires en mémoire) : pour les trois instructions ;
- actions de suppression (delete actions) : pour delete_from uniquement, contournable par skipDeleteActions ;
- journal de base de données activé sur la table : contournable par skipDatabaseLog ;
- méthode insert, update ou delete surchargée : contournable par skipDataMethods ;
- alertes configurées sur la table : contournable par skipEvents ;
- propriété ValidTimeStateFieldType différente de None : pas de contournement.
Attention : si la conversion a lieu malgré tout, les réglages skip* sont ignorés. Et ignorer les méthodes de données saute aussi la logique métier qu'elles contiennent ; ne le faites que si vous savez que cette logique n'a pas à s'exécuter.
Erreurs fréquentes
- Écrire un update_recordset sur une table dont update() est surchargée et croire que l'opération est ensembliste : elle tourne en boucle.
- Ajouter skipDataMethods(true) pour « accélérer » sans vérifier ce que fait la méthode surchargée : contrôles et mises à jour liées sont sautés.
- Oublier ttsBegin / ttsCommit autour des mises à jour et suppressions.
- Mal aligner l'ordre des champs entre la cible et le select d'un insert_recordset.
- Utiliser une boucle avec insert() pour remplir la table temporaire d'un état : un insert_recordset est souvent possible. Voir rapport SSRS avec classes RDP et Contract.
Dans quel contexte l'utiliser ?
Traitements batch de purge, de recalcul ou de copie, alimentation de tables temporaires d'états, migrations de données : voir batch SysOperation pas à pas. Lors d'une revue de code, qu'il soit écrit par un humain ou généré par IA, la question « peut-on passer en ensembliste ? » fait partie des contrôles de base. Skalp AI génère le code X++ avec tests unitaires, livré sur votre Azure DevOps et validé par votre DSI : voir développement X++ par IA, développer en X++ sans consultant en 2026 et les offres Basic, Pro et Max.
Sources
- Microsoft Learn, « Insert data », mise à jour du 31/03/2026, consultée le 11/10/2026.
- Microsoft Learn, « Update data », mise à jour du 31/03/2026, consultée le 11/10/2026.
- Microsoft Learn, « Delete data », consultée le 11/10/2026.
- Microsoft Learn, « Conversion of operations from set-based to record-by-record », mise à jour du 31/03/2026, consultée le 11/10/2026.
Questions fréquentes
insert_recordset ou RecordInsertList ?
insert_recordset est plus rapide quand les données viennent directement d'autres tables. RecordInsertList est plus souple si vous devez préparer chaque ligne en X++ avant l'insertion ; il regroupe aussi les insertions en un appel.
Pourquoi mon update_recordset est-il lent ?
Il est probablement repassé en ligne par ligne : méthode update surchargée, journal de base de données, alertes ou table à validité temporelle. Vérifiez ces points avant d'envisager skipDataMethods, skipDatabaseLog ou skipEvents.
Les méthodes skip* sont-elles toujours prises en compte ?
Non. Selon Microsoft Learn, si l'opération est malgré tout convertie en ligne par ligne, tous les réglages skip* sont ignorés.
Prêt à transformer votre D365 ?
Rejoignez les DSI qui développent leurs tickets D365 en 1h pour 1 crédit (≈ 400 € en Basic, 200 € en Pro).