Maîtriser la fonction LookUp dans Power Apps : guide technique approfondi
La fonction LookUp est l’une des plus utilisées dans Power Apps, mais aussi l’une des plus mal comprise. Elle permet de rechercher et retourner un enregistrement ou une valeur précise dans une table selon une condition. Apparemment simple, elle cache des subtilités de performance, de délégation et de comportement qui peuvent faire la différence entre une app fluide et une app qui rame.
Dans ce guide technique, je vais tenter d’explorer :
-
Les fondamentaux et la syntaxe complète
-
Le modèle d’exécution et la délégation
-
Les patterns avancés et anti-patterns
-
L’optimisation des performances
-
Les cas d’usage réels en production
1. Anatomie de LookUp : au-delà de la syntaxe
Syntaxe complète
LookUp(Source; Condition; Result)
-
Source : Table, collection, ou source de données connectée
-
Condition : Expression booléenne évaluée pour chaque enregistrement
-
Result (optionnel) : Expression retournant la valeur souhaitée (pas seulement un nom de colonne)
Ce que retourne LookUp
-
Si Result est omis → retourne l’enregistrement complet (record)
-
Si Result est spécifié → retourne la valeur évaluée (peut être une formule)
-
Si aucune correspondance → retourne
Blank() -
LookUp retourne toujours le premier enregistrement correspondant selon l’ordre de la source
⚠️ Point critique : L’ordre n’est PAS aléatoire, mais il est non déterministe si la source n’est pas explicitement triée. Sur SharePoint, c’est souvent l’ordre d’ID. Sur Dataverse, ça dépend de l’index utilisé.
2. Délégation : comprendre les limites réelles
Qu’est-ce qui est délégable ?
La délégation de LookUp dépend de trois facteurs :
-
La source de données
-
Les opérateurs utilisés dans la condition
-
Les fonctions utilisées dans Result
Tableau de délégation par source
| Source | LookUp délégable ? | Opérateurs supportés | Limite sans délégation |
|---|---|---|---|
| Dataverse | ✅ Oui | =, <>, <, >, <=, >=, And, Or, Not, In, StartsWith |
500-2000 lignes |
| SharePoint | ⚠️ Partiel | =, <>, IsBlank uniquement |
500-2000 lignes |
| SQL Server | ✅ Oui | Tous opérateurs standards | 500-2000 lignes |
| Collections | ❌ Non | Tous (mais en local) | Toutes les lignes (mémoire) |
| Excel | ❌ Non | Tous | 500-2000 lignes |
Exemple de délégation cassée sur SharePoint
`// ❌ NON DÉLÉGABLE sur SharePoint (Contains non supporté)
LookUp(Documents; Contains(Title; "rapport"); ‘Created By’)
// ✅ DÉLÉGABLE sur SharePoint
LookUp(Documents; StartsWith(Title; "rapport"); ‘Created By’)`
Comment vérifier la délégation ?
Dans Power Apps Studio, les formules non délégables affichent un **triangle ** avec l’avertissement "delegation warning".
3. Modèle d’exécution et performance
Où et quand LookUp s’exécute
Le contexte d’exécution de LookUp est crucial pour les performances :
| Propriété | Évaluation | Impact performance | Usage recommandé |
|---|---|---|---|
| OnSelect | Une fois au clic | ✅ Faible | Idéal pour actions ponctuelles |
| OnVisible | Au chargement de l’écran | ✅ Faible | Excellent pour initialisation |
| Default | Au chargement du contrôle | ⚠️ Moyen | Acceptable si source < 1000 lignes |
| Text / Items | À chaque rafraîchissement | ❌ Élevé | À ÉVITER absolument |
| DisplayMode / Visible | À chaque interaction | ❌ Très élevé | Anti-pattern majeur |
Erreur classique déjà observée
Label1.Text = LookUp(Employees; Email = TextInput1.Text; FullName)
Pourquoi est-ce une erreur? Chaque caractère tapé réévalue la formule. Sur une table de 5000 employés non délégable, cela provoque un freeze de l’app.
Pattern correct
`
TextInput1.OnChange:
Set(varEmployeeName;
LookUp(Employees; Email = TextInput1.Text; FullName)
)
Label1.Text:
varEmployeeName`
4. Techniques avancées
a) LookUp avec formule dans Result
Result peut être une expression, pas seulement un nom de colonne :
`// Calcul direct dans Result
LookUp(Orders;
OrderID = 12345;
Quantity * UnitPrice * (1 – Discount)
)
// Concaténation
LookUp(Employees;
ID = varEmployeeID;
FirstName & " " & LastName & " (" & Department & ")"
)`
b) Gérer Blank() proprement
Méthode classique avec IsBlank
If( IsBlank(LookUp(Orders; Status = "Open"; OrderID)); "Aucune commande"; "Commande trouvée" )
Méthode moderne avec coalesce (//)
LookUp(Orders; Status = "Open"; OrderID) // "N/A"
⚠️ Piège : Le coalesce ne fonctionne que si les types correspondent. Si OrderID est un nombre et "N/A" une chaîne, erreur de type.
c) LookUp dans Patch – attention à ce piège silencieux
Patch(Orders; LookUp(Orders; OrderID = 9999); {Status: "Closed"} )
Problème : Si OrderID 9999 n’existe pas, LookUp retourne Blank(). Patch avec un premier argument Blank() crée un nouvel enregistrement au lieu de mettre à jour.
Solution robuste
With( {record: LookUp(Orders; OrderID = 9999)}; If( !IsBlank(record); Patch(Orders; record; {Status: "Closed"}); Notify("Commande introuvable"; NotificationType.Error) ) )
d) LookUp vs First(Filter()) – quand choisir ?
LookUp(Products; SKU = "A123") First(Filter(Products; SKU = "A123"))
Différences subtiles :
| Critère | LookUp | First(Filter()) |
|---|---|---|
| Lisibilité | ✅ Plus clair | Moins direct |
| Délégation | Même capacité | Même capacité |
| Chaînage | Impossible | Possible avec Sort() |
| Performance | Identique | Identique |
Quand utiliser First(Filter()) :
// Besoin de trier avant de prendre le premier First( Sort( Filter(Orders; CustomerID = 1001); OrderDate; Descending ) )
LookUp seul ne permet pas de contrôler l’ordre. Si l’ordre importe, utilisez First(Sort(Filter())).
5. Relations Dataverse et navigation
LookUp dans les relations many-to-one
`// Navigation depuis un enregistrement vers sa relation
Gallery1.Selected.Account.’Account Name’
// Équivalent avec LookUp (moins performant)
LookUp(Accounts;
‘Account ID’ = Gallery1.Selected.’Account ID’;
‘Account Name’
)`
Recommandation : Sur Dataverse, privilégiez la navigation par relation native. C’est plus performant et plus lisible.
LookUp dans les choix (Choice columns)
`// ❌ ERREUR FRÉQUENTE
LookUp(Contacts; Status = "Active")
// ✅ CORRECT – Les choix sont des enums
LookUp(Contacts; Status = Status.Active)`
6. LookUp imbriqués – à utiliser avec parcimonie
// Récupérer le nom du manager du manager LookUp(Employees; ID = LookUp(Employees; ID = varCurrentEmployeeID; ManagerID ); FullName )
Problèmes :
-
Performance : Deux requêtes potentiellement non délégables
-
Lisibilité : Code difficile à maintenir
-
Debugging : Impossible de savoir quelle étape échoue
Alternative recommandée :
// Découper en variables Set(varManagerID; LookUp(Employees; ID = varCurrentEmployeeID; ManagerID) ); Set(varManagerOfManagerName; LookUp(Employees; ID = varManagerID; FullName) );
7. Patterns de cache et optimisation
Pattern : Cache avec expiration
`// OnVisible de l’écran
If(
DateDiff(varLastRefresh; Now(); Minutes) > 5 || IsBlank(varEmployeeData);
Set(varEmployeeData;
LookUp(Employees; Email = User().Email)
);
Set(varLastRefresh; Now())
)
// Utilisation dans l’app
Label1.Text = varEmployeeData.FullName
Label2.Text = varEmployeeData.Department`
Avantages :
-
Une seule requête au lieu de N
-
Contrôle de la fraîcheur des données
-
Performance maximale
Pattern : Lazy loading conditionnel
// Ne charge que si nécessaire If( IsBlank(varProductDetails); Set(varProductDetails; LookUp(Products; SKU = varSelectedSKU) ) )
8. Debugging et troubleshooting
Techniques de débogage
`// Afficher le résultat complet dans un Label
Label_Debug.Text = JSON(
LookUp(Orders; OrderID = 12345);
JSONFormat.IncludeBinaryData
)
// Vérifier si le résultat est Blank
Label_Debug.Text = If(
IsBlank(LookUp(Orders; OrderID = 12345));
"BLANK RESULT";
"FOUND"
)
// Compter les résultats potentiels
CountRows(Filter(Orders; Status = "Open"))`
Erreurs courantes et solutions
| Erreur | Cause | Solution |
|---|---|---|
| "Invalid argument type" | Type mismatch dans condition | Vérifier les types (Text vs Number) |
| "Network error" | Délégation échouée | Vérifier les limites de données |
| Résultat toujours Blank | Condition jamais vraie | Tester avec CountRows(Filter()) |
| Performance dégradée | LookUp dans propriété "live" | Déplacer vers OnVisible/OnSelect |
10. Checklist des bonnes pratiques
✅ Toujours vérifier la délégation sur les sources > 2000 lignes
✅ Utiliser LookUp uniquement pour un résultat unique attendu
✅ Mettre en cache les résultats réutilisés (Set/With)
✅ Éviter LookUp dans Text/Visible/DisplayMode sans cache
✅ Gérer explicitement Blank() avec IsBlank ou //
✅ Sécuriser les Patch avec vérification IsBlank
✅ Préférer les relations natives sur Dataverse
✅ Découper les LookUp imbriqués en variables
✅ Tester avec des volumes réalistes (pas seulement 10 lignes)
✅ Documenter les hypothèses sur l’ordre des résultats
Conclusion
La fonction LookUp est un outil fondamental de Power Apps, mais son utilisation efficace nécessite une compréhension approfondie de :
-
La délégation et ses limites par source de données
-
Le contexte d’exécution et son impact sur les performances
-
La gestion des cas limites (Blank, types, erreurs)
-
Les patterns d’optimisation (cache, lazy loading)
Règle d’or : LookUp est votre allié pour cibler rapidement une donnée précise, mais uniquement si vous anticipez ses comportements et optimisez son contexte d’exécution. Une formule LookUp mal placée peut transformer une app fluide en cauchemar utilisateur.