La requête SOQL (Salesforce Object Query Language) est l’outil qui permet de lire dans la base de données dans Salesforce, que ce soit dans du code Apex ou pour exporter des données.
Une requête SOQL est constituée de blocs (à la manière de propositions dans une phrase en français) qui commencent toujours par un mot-clé. Cet article présente ces mots-clés et les fonctionnalités qu’ils permettent. Par convention, les mots spécifiques aux requêtes seront écrits en majuscule, mais ce n’est pas obligatoire pour leur fonctionnement.
Nous allons voir différents exemples de requêtes, en expliquant à chaque fois les nouveaux mots-clés et en présentant leur fonctionnement.
Niveau 1 – Structure d’une requête basique
Voyons une première requête simple pour nous familiariser avec les bases d’une requête SOQL :
SELECT Id, Name
FROM Account
WHERE Name = 'Toto'
LIMIT 20
Ici, on peut déjà distinguer 4 blocs :
SELECT
: permet de préciser la liste des champs que l’on souhaite récupérer, séparés par des virgules (dans ce cas, l’identifiant est le nom).FROM
: identifie le type d’objet que l’on veut récupérer (dans le cas présent, les comptes).WHERE
: rajoute un filtre sur la requête. Seuls les enregistrements qui répondent à cette condition seront retournés (ici, les comptes dont le nom est égal à ‘Toto’).LIMIT
: permet de limiter le nombre d’enregistrements qui seront retournés (ici, pas plus de 20).
Les deux mots-clés vraiment indispensables sont SELECT
et FROM
, les autres étant facultatifs.
Niveau 2 – Logique de filtres et ordre
La requête précédente présentait un filtre simple, mais il est possible d’utiliser plusieurs champs pour affiner la recherche :
SELECT Id, Account.Name
FROM Case
WHERE CreatedDate = LAST_QUARTER
AND (NOT (ContactEmail != null OR ContactMobile != null))
ORDER BY LastModifiedDate DESC
LIMIT 10
OFFSET 10
LAST_QUARTER
: C’est une expression littérale qui représente une période donnée relative à la date d’aujourd’hui. Dans cet exemple, seules les requêtes ayant été créées pendant le trimestre précédent seront récupérées.AND / OR / NOT
: permet de combiner plusieurs critères dans le filtreWHERE
, à partir de la logique spécifiée (on utilise des parenthèses pour préciser l’ordre de priorité).ORDER BY
: permet d’ordonner les résultats selon un ou plusieurs champs définis. L’ordre peut être ascendant (ASC
, valeur par défaut) ou descendant (DESC
). Ici, les requêtes ayant été modifiées le plus récemment apparaîtront en premier.OFFSET
: permet d’ignorer un certain nombre de résultats retournés. Cela peut être utile en combinaison avecLIMIT
dans le cadre d’un système de pagination, par exemple, pour ne récupérer que les résultats correspondant à une page en particulier.
Niveau 3 – Filtres avancés et corbeille
Pour terminer sur les filtres, il existe des fonctions plus avancées qui peuvent répondre à des besoins spécifiques :
SELECT Name
FROM Opportunity
WHERE StageName IN ('Prospecting', 'Qualification')
AND Name LIKE '_Force%'
AND Multipicklist__c INCLUDES ('Value1')
ALL ROWS
IN / NOT IN
: permet de tester un champ parmi une liste de valeurs, et plus une seule comme avec l’opérateur ‘=
‘.LIKE
: il s’agit d’une comparaison d’un texte avec un format particulier. Le symbole ‘_
‘ correspond à n’importe quel caractère, et le symbole ‘%
‘ à n’importe quelle chaîne de caractères (y compris vide).INCLUDES / EXCLUDES
: est un opérateur de comparaison utilisé pour les champs multi-picklists (listes déroulantes à choix multiples), et permet de vérifier qu’une valeur y est présente ou pas.ALL ROWS
: permet d’étendre la requête aux enregistrements placés dans la corbeille et aux activités archivées, qui ne sont pas pris en compte autrement.
Niveau 4 – Requêtes imbriquées, Scopes
Lorsque l’on fait une requête sur un objet, il est possible d’en récupérer également les enregistrements liés :
SELECT FIELDS(STANDARD),
(SELECT Id, Name FROM Opportunities WHERE Amount >= 10)
FROM Account
USING SCOPE Mine
FIELDS
: est une fonction qui permet de récupérer l’ensemble des champsSTANDARD
,CUSTOM
ou tous les champs (FIELDS(ALL)
)(SELECT * FROM *)
: il s’agit d’une requête imbriquée qui va récupérer une liste d’enregistrements d’un objet enfant. Ici par exemple, chaque compte aura une colonne contenant la liste de ses opportunités liées.USING SCOPE
: permet de définir la visibilité des enregistrements récupérés. Cet exemple ne retournera que les comptes dont l’utilisateur est propriétaire. On peut également ne demander que les enregistrements appartenant à son équipe, par exemple.
Niveau 5 – Groupements
Dans certains cas, il peut être intéressant de récupérer un groupe d’enregistrements. Ceci permet de faire différents calculs sur les lignes de ce groupe :
SELECT LeadSource,
COUNT(Id),
AVG(Amount),
MAX(CloseDate)
FROM Opportunity
WITH SECURITY_ENFORCED
GROUP BY LeadSource
GROUP BY
: groupe les résultats selon un champ donné. Une requête groupée renverra une liste d’objets de typeAggregateResult
qui contiendra les groupes créés. Chaque groupe contient les éléments présents dans leSELECT
et accessibles en Apex via la méthode get.COUNT / AVG / MAX
: ce sont des fonctions d’agrégation à utiliser avecGROUP BY
qui permettent de faire des calculs sur les éléments d’un groupe. Par exemple,AVG
prendra la moyenne du champ spécifié,MAX / MIN
en prendront les valeurs maximales / minimales.COUNT
fera le compte des enregistrements qui ont une valeur dans le champ spécifié. Dans notre exemple, chaque ligne contiendra le nombre d’enregistrements, la moyenne du champAmount
et la valeur maximale du champCloseDate
.WITH SECURITY_ENFORCED
: permet d’appliquer les permissions concernant les champs et les objets à la requête. Si l’utilisateur qui exécute la requête n’a pas les droits sur un champ, il ne pourra pas le récupérer.
Il existe également des fonctions pour grouper par dates, par exemple par mois, par année fiscale ou par trimestre (à la manière des expressions littérales vues plus tôt). À noter qu’en groupant les enregistrements, on perd l’information des enregistrements individuels : on ne peut sélectionner que les champs par lesquels on groupe et des agrégats.
Niveau 6 – Fonctions avancées des groupes
La requête précédente montrait la possibilité de grouper les enregistrements selon un champ. Il est également possible de les grouper selon plusieurs champs, et de filtrer les groupes :
SELECT AccountId, GROUPING(AccountId) grpAcc,
Type, GROUPING(Type) grpType,
SUM(Amount),
MIN(CloseDate)
FROM Opportunity
GROUP BY ROLLUP(AccountId, Type)
HAVING COUNT(ID) > 3
Ici on peut constater l’utilisation d’alias après les fonctions d’agrégation (‘grpAcc
‘ dans ‘GROUPING(AccountId) grpAcc
‘), qui permettent d’y faire référence plus facilement.
ROLLUP
: cette fonction se combine à unGROUP BY
pour faire calculer et combiner des sous-totaux des agrégats sélectionnés. Dans cet exemple, la requête renverra une ligne pour chaque couple AccountId/Type, une ligne de sous-total pour chaque valeur de AccountId, et une ligne rassemblant tous les enregistrements (tous les comptes).GROUPING(champ)
: vaut 1 si la ligne est un sous-total du type spécifié et 0 sinon. Dans cet exemple, les lignes qui rassemblent tous les types (correspondant à un compte) aurontgrpType = 1
, tandis que les lignes correspondant à un couple AccountId/Type aurontgrpType = 0
.HAVING
: définit une condition que doit remplir les groupes (et non plus les enregistrements) pour être prise en compte dans la requête. Ici, seuls les groupes contenant plus de 3 enregistrements apparaîtront. C’est l’équivalent duWHERE
, s’appliquant aux groupes.
Notez aussi l’existence du mot-clé GROUP BY CUBE
, variante de ROLLUP
, qui calculera des sous-totaux pour toutes les combinaisons des champs. Pour reprendre l’exemple ci-dessus, l’utilisation de GROUP BY CUBE
renverra les mêmes lignes qu’avec ROLLUP
, mais rajoutera également les sous-totaux pour chaque valeur de Type.
Niveau 7 – Relations polymorphiques
Une relation polymorphique est un champ qui peut lier plusieurs objets différents, contrairement aux look-ups (relation de recherche) ou master-detail (maître détail), qui ne peuvent n’en représenter qu’un seul. Pour ces relations, les champs à récupérer vont dépendre de l’objet contenu dans la relation, on utilise donc une syntaxe spécifique :
SELECT
TYPEOF What
WHEN Account THEN Phone, NumberOfEmployees
WHEN Opportunity THEN Amount, CloseDate
ELSE Name, Email
END
FROM Task
FOR UPDATE
Ici, on récupère des champs différents selon que la relation pointe vers un compte, une opportunité ou un autre objet.
TYPEOF / END
: spécifie la relation polymorphique que l’on souhaite utiliser dans le filtre, leEND
termine le block.-
WHEN / THEN / ELSE
: permet de préciser les champs à requêter pour chaque objet (leELSE
représentant toutes les situations qui n’auraient pas été traitées auparavant). FOR
: spécifie un traitement particulier à appliquer aux enregistrements récupérés, comme par exemple, le fait de les verrouiller dans le but de les mettre à jour sans avoir à se préoccuper de conflits potentiels avecFOR UPDATE
. Il existe aussiFOR VIEW
etFOR REFERENCE
, qui permettent de prendre en compte des enregistrements dans la liste des « Récemment visualisés ».
Niveau 8 – Cas particuliers des requêtes SOQL
Certains objets offrent des fonctionnalités particulières pour les requêtes SOQL, comme par exemple, les articles « Knowledge » de Salesforce :
SELECT Title
FROM KnowledgeArticleVersion
WITH DATA CATEGORY Group1__c ABOVE SubCategory1__c
WITH
DATA CATEGORY
: permet de filtrer les articles en fonction de leur catégorie dans la hiérarchie des catégories dans Knowledge. C’est donc un filtre qui n’ira chercher les articles que dans les catégories répondant au critère spécifié (ici, ceux donc la catégorie est au-dessus deSubCategory1__c
). Il existe plusieurs sélecteurs pour déterminer où chercher l’article à partir de la catégorie donnée (AT
,BELOW
,ABOVE_OR_BELOW
).
Le mot-clé WITH
peut également s’utiliser dans d’autres cas spécifiques décrits dans la documentation.
Pour résumer
Nous avons vu des exemples de requêtes SOQL permettant de mettre en évidence les différentes fonctionnalités à notre disposition. La liste n’est cependant pas exhaustive et certaines possibilités n’ont pas été présentées dans cet article.
Notez qu’il existe un certain nombre de limites qui peuvent s’appliquer à ces requêtes, comme par exemple, une limite dans le nombre de lignes qui peuvent être retournées d’un coup, ainsi que des limites associées à certains objets. Il est aussi important que les mots-clés apparaissent dans un certain ordre pour que la requête fonctionne.
La documentation officielle rentre plus dans les détails de chaque fonctionnalité pour en expliquer les usages et limites.
Si vous souhaitez en savoir plus sur SOQL, découvrez l’article de Fabien Taillon !