Validierung einer Bedingung: Formel vs. Query

Immer wieder stößt man in der Programmierung auf Bedingungen. Nur wenn diese Bedingungen zutreffen, findet eine bestimmte Verarbeitung statt. Ich möchte in diesem Artikel vorstellen, auf welche Arten Ausführungen in Salesforce an Bedingungen geknüpft werden können und anschließend untersuchen welche Methoden dabei am effektivsten sind. Im Besonderen gehe ich dabei auf die Benutzung von Formeln ein.

Die Möglichkeiten:

Die einfachste Art Bedingungen abzufragen ist das, was Salesforce einem an ‚Board-Mitteln‘ mit gibt. Hierfür sind keine Programmierkenntnisse erforderlich. Für komplexere Bedingungen sollte man aber analytisches Denkvermögen mitbringen. Außerdem sollte man einen genauen Einblick in die Prozesse der Salesforce-Organisation haben.

Legt man z.B. einen Workflow an, so kann man dort Kriterien hinzufügen. Im folgenden Beispiel lautet das Kriterium, dass der Account Name nicht leer sein darf.

Treffen diese Kriterien auf einen Datensatz zu, werden die Aktionen ausgeführt, die man im nächsten Schritt des Konfigurators festlegt.

Komplexere Abfragen und Aktionen lassen sich mittels Apex programmieren. Dabei wird Programmcode, den man selber schreibt, auf dem Salesforce-Server ausgeführt. Man arbeitet hier mit sogenannten ‚Triggern‚. Diese erlauben es bei bestimmten Ereignissen Code auszuführen. Um beim obigen Beispiel zu bleiben, würde man einen Trigger anlegen, der ausgeführt wird, wenn ein Account neu eingefügt oder bearbeitet wird. Der Quellcode dafür würde dann etwa so lauten:

trigger checkAccountName on Account (before insert, before update) {
    for(Account account : trigger.new) {
        if(account.name != null) {
        // do something...
        }
    }
}

Wann nehme ich welche Lösung?

Wo immer möglich, sollte man mit den Mitteln arbeiten, die per Mausklick im Setup zusammengestellt werden können. Gerade die kleinen Helfer des Alltags lassen sich oft ohne eigene Programmierung umsetzen. Eigens programmierter Code muss getestet und deployed werden. Doch das ist ja  in Salesforce zum Glück ganz einfach. Ich bin ein großer Fürsprecher der ‚Test-first‚ Methode. Auch für Umsetzungen ohne eigenen Code ist es sehr sinnvoll Tests zu schreiben, da diese das erwartete Verhalten verifizieren. So kann man etwas bauen, den Test ausführen und merkt sofort, ob noch alles stimmt.

Einsatz von Formeln

Wenn Trigger zum Einsatz kommen, verwende ich sehr gerne Formeln. Prüft der Trigger auf eine Reihe von Bedingungen, die zutreffen müssen, so kann man dafür eine Formel bauen. Gibt diese Formel ‚0‘ zurück, wird nicht fortgefahren, gibt sie ‚1‘ zurück, so wird ausgeführt. Dies hat unter anderem diese Vorteile:

  • Die Bedingungen lassen sich ändern, ohne neu deployen zu müssen
  • Die Formel kann von verschiedensten Stellen referenziert werden: Workflow, Trigger, Controller…
  • Der Code wird dadurch übersichtlicher und weniger Zeilen zählen gegen das Org.-Limit
  • Formeln können von Haus aus auf Übergeordnete Objekte zugreifen

Wie der Einsatz einer solchen Formel konkret aussehen kann, wird im nächsten Absatz ersichtlich. Dort sieht man auch, dass eine Formel ohne eine Abfrage (SOQL Select) auszuführen auf das übergeordnete Objekt zugreifen kann.

Formel vs. Query

Das Szenario: In einem Tochter-Objekt von Account („Account child“) soll ein Stückchen Apex code ausgeführt werden. Für die Bedingung werden zwei Felder des Tochter-Objektes und eins vom Account herangezogen. Die auszuführende Methode ist nicht weiter Gegenstand dieser Untersuchung. Interessant ist, auf welche Arten im Trigger evaluiert werden kann, ob die Methode ausgeführt werden soll oder nicht.

Mich interessiert außerdem, ob das Evaluieren einer Formel schneller ist als das Ausführen eines Selects. Deshalb  habe ich für den Test zwei identische Benutzerdefinierte Objekt angelegt, die die folgenden Felder enthalten.

  • Checkbox, eine Checkbox
  • email, ein E-Mail Feld
  • Account, ein Nachschlagefeld auf Account

Account_Child_02 hat zusätzlich noch eine Formel, die die Bedingungen überprüft und „1“ zurück gibt, wenn die Bedingungen alle zutreffen.

Die Bedingungen für eine Ausführung lauten:

  • Account_child.Checkbox muss wahr (angehakt) sein
  • Account_child.email darf nicht leer sein
  • Account_child.Account.Name darf nicht leer sein

Möglichkeit 1: Evaluierung mittels SOQL Select

Der folgende Trigger baut zuerst eine Map der Objekte, die in den Trigger gereicht werden, um Bulk-fähig zu sein. Die Map ist nötig, da im Trigger nicht automatisch Felder aus den Eltern-Objekten zur Verfügung stehen. Diese müssen erst durch einen SOQL-Select verfügbar gemacht werden. In der dritten Bedingung der ‚If-Clause‘ wird darauf zurückgegriffen.

trigger AccountChild01 on Account_Child_01__c (after insert, after update) {
    
    // In order to be bulk-able build a Set of the Account Ids
    // and then query Accounts and store them in a Map
    
    // First initiate the Set
    Set accountIds = new Set();
    // Now fill the Set with IDs
    for(Account_Child_01__c account_child : trigger.new) {
        accountIds.add(account_child.Account__c);
    }
    
    // Initiate a Map and Put the Accounts in it
    Map accounts = new Map([
        SELECT Id, BillingCity 
        FROM Account 
        WHERE Id IN :accountIds]);
    
    // Now for the real part, iterate over the Account Children
    for(Account_Child_01__c account_child : trigger.new) {
        // Check for Criteria
        if(account_child.Checkbox__c == true && 
            account_child.email__c != null && 
            accounts.get(account_child.Account__c).BillingCity == 'Hamburg') {
            //System.debug('Criteria match for Account Child 01');
            // Process logic here...
        }
    }
}

Möglichkeit 2: Evaluierung mittels einer Formel

Dieser Trigger prüft auch, ob die einzelnen Objekt weiter bearbeitet werden. Die Kriterien sind genau die gleichen wie bei ‚Möglichkeit 1‘, nur, dass hier auf den Rückgabewert einer Formel geprüft wird.

Die Formel sieht wie folgt aus:

IF( Checkbox__c,  
IF(ISBLANK( email__c ),0,  
IF( Account__r.BillingCity = "Hamburg", 1, 0) ) , 0)

Hier wird nacheinander geprüft, ob die Checkbox gesetzt, email nicht leer und Rechnungsanschrift des Accounts gleich Hamburg ist. Treffen alle diese Kriterien zu,l gibt die Formel eine 1 zurück. Ansonsten 0.

Der Trigger ist wesentlich übersichtlicher als im ersten Beispiel:

trigger AccountChild02 on Account_Child_02__c (after insert, after update) {

  // Iterate over the Account Children
  for(Account_Child_02__c account_child : trigger.new) {
    // Check for Criteria
    if(account_child.Formula_Continue__c == 1) {
      //System.debug('Criteria match for Account Child 02');
      // Process more logic here
    }
  }

}

Das aufwändige Bauen einer Map fällt komplett weg.

Geschwindigkeit

Mittels einer Test-Klasse möchte ich sehen, welche Möglichkeit schneller ausgeführt wird. Dazu füge ich jeweils 250 Accounts und 250 Account_Children ein. Meine Erwartung ist, dass der Trigger mit Formel schneller ausgeführt wird, weil die Formel schon auf Datenbank-Ebene berechnet werden sollte.

Tatsächlich ist das Ergebnis aber ein anderes:
Möglichkeit 2 (mit Formel) braucht im Mittel 200 Millisekunden länger bei der Ausführung als der Trigger in der zunächst noch eine Map gebaut und ein SOQL-Select ausgeführt wird.

2 thoughts on “Validierung einer Bedingung: Formel vs. Query

  1. Interessanter Artikel, auch mich verwundert das Ergebniss des Geschwindigkeitsbenchmarks. Es wäre nur interessant zu erfahren was genau 200 Millisekunden mehr bedeutet. 200 gegen 250ms wären verherend 5000 gegen 5200ms verschmerzbar wenn man Nichtentwickler nicht unnötig belasten möchte.

  2. Hey Marius,

    vielen Dank für das Feedback.

    Generell ist es so, dass bei der Arbeit mit Salesforce die Kommunikation des Clients (==Browser), mit dem SFDC Server in den USA, am meisten Zeit in Anspruch nimmt. Die Server-seitige Verzögerung fällt selten ins Gewicht.

    Bei dem Test mit vielen Accounts und vielen Account_Children dauert die Ausführung etwa 2000 MS mit einem Unterschied zwischen Select und Formel von 200 MS. Für nur einen Account plus einem Children dauert es um die 60 MS mit einem Unterschied von 10 MS.
    Ich habe heute den ganzen Test nochmal durchgeführt (auf NA7) und festgestellt, dass die Formel jetzt schneller ist als der Select. Da wurde sicher in der Zwischenzeit was an den Optimierungs-Schichten gedreht (Vergleich: Salesforce Whitepaper).

    Beide Lösungen sind also performant – je nach Saison 😉

    Für die genauen Zahlen kannst du gerne in dieses XLS sheet gucken: http://salesforce-blog.de/wp-content/uploads/account_children_benchmark.xlsx
    Die Zahlen vom ersten Test habe ich leider nicht mehr zur Hand.

    Cheers,
    //Hannes

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.