Sunday, 30 May 2021

Invocable Methods in Apex

 The Invocable method will allow an Apex method to be executed as an invocable action.


Note :


Use the InvocableVariable annotation to identify variables used by invocable methods in custom classes.


The InvocableVariable annotation identifies a class variable used as an input or output parameter for an InvocableMethod method’s invocable action. 

If you create your own custom class to use as the input or output to an invocable method, you can annotate individual class member variables to make 

them available to the method.


ex :


public with sharing class InvocableApexExample {



  @InvocableMethod(label='Example Invocable Method' description='Example Invocable Method')

    public static List<ContactSearchResult> findRelatedContacts(List<ContactSearchRequest> inputParams) {

        List<SObject> inputs = inputParams[0].inputs;

        String firstRecordId = inputs[0].Id;

        String sObjectType = inputs[0].Id.getSobjectType().getDescribe().getName();

         String queryString = '';

        switch on sObjectType.toLowerCase() {

            when 'account' {

                queryString = 'SELECT Id, Name FROM Contact WHERE accountId = :firstRecordId WITH SECURITY_ENFORCED LIMIT 1';

            }

            when 'task' {

                Task task = (Task) inputs[0];

                String whoId = task.WhoId;

                queryString = 'SELECT Id, Name FROM Contact WHERE Id = :whoId WITH SECURITY_ENFORCED LIMIT 1';

            }

            when else {

                throw new InvocableMethodRecipesException(

                    'Unknown object type passed. This method only supports Account and Task.'

                );

            }

        }

        

         Contact contact = Database.query(queryString);

         List<ContactSearchResult> results = new List<ContactSearchResult>();

         results.add(new ContactSearchResult(contact));

        

        return results;

    }

  

  

  // This class Represents Input to the InvocableMethod 

 

 Public class ContactSearchRequest { 

    @InvocableVariable(label='Generic SObject records - Input' description='Input variable for generic Sobjects' required=true)

    Public List<SObject> inputs; 

 }

 

 // This class Represents output from the InvocableMethod

 

  public class ContactSearchResult {

        @InvocableVariable(label='Generic SObject record - Output' description='Output variable for generic SObject' required=true)        

        public SObject output;       

        public ContactSearchResult(SObject toOutput) {

            this.output = toOutput;

        }

    }

    

    // Internal custom exception class

 

   public class InvocableMethodRecipesException extends Exception {

   

     }

 

}



Implementation Notes :


1.The invocable method must be static and public or global, and its class must be an outer class.

2.Only one method in a class can have the InvocableMethod annotation.

3.Other annotations can’t be used with the InvocableMethod annotation.


Inputs and Outputs :


There can be at most one input parameter and its data type must be one of the following:


1.A list of a primitive data type or a list of lists of a primitive data type – the generic Object type is not supported.

2.A list of an sObject type or a list of lists of an sObject type.

3.A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).

4.A list of a user-defined type, containing variables of the supported types above or user-defined Apex types, with the InvocableVariable annotation. 

Create a custom global or public Apex class to implement your data type, and make sure that your class contains at least one member variable 

with the invocable variable annotation.


If the return type is not Null, the data type returned by the method must be one of the following:


1.A list of a primitive data type or a list of lists of a primitive data type – the generic Object type is not supported.

2.A list of an sObject type or a list of lists of an sObject type.

3.A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).

4.A list of a user-defined type, containing variables of the supported types above or user-defined Apex types, with the InvocableVariable annotation. 

Create a custom global or public Apex class to implement your data type, and make sure that your class contains at least one member variable 

with the invocable variable annotation.


Managed Packages :


1.You can use invocable methods in packages, but once you add an invocable method you can’t remove it from later versions of the package.

2.Public invocable methods can be referred to by flows and processes within the managed package.

3.Global invocable methods can be referred to anywhere in the subscriber org. Only global invocable methods appear in Flow Builder 

and Process Builder in the subscriber org.

Field History Tracking records in Salesforce

 If field history tracking is enabled for an object, the changes are stored in history table for that particular object.


The API name of these tables follows a simple convention so should be easy to find. For standard objects, 

the name of the history table follows the format: 'ObjectNameHistory' so for the Account object the history table is AccountHistory.


For custom objects, the name of the convention simply replaces the 'c' on the end of the API name with 'History'. 

So, for a custom object call My_Custom_Object__c the history table is called My_Custom_Object__History.


ex :


SELECT AccountId, OldValue, NewValue, IsDeleted, Id, Field, CreatedBy.Name From AccountHistory WHERE AccountId=:accountId ORDER BY CreatedDate DESC


SELECT Id, Name, (Select OldValue, NewValue From Histories) FROM My_Custom_Object__c


SELECT OldValue, NewValue, Parent.Id, Parent.name, Parent.customfield__c FROM My_Custom_Object__history



SELECT Name, customfield__c, (SELECT ParentId, OldValue, NewValue,Field FROM Histories ) FROM Custom_Object__c


Note : The child history relationship name is called "histories".


SELECT ID, Subject, Type, (SELECT Field, OldValue, NewValue FROM Histories) FROM Case



Field History Tracking in Salesforce :


Out of the box Salesforce can track up to 20 fields per object using field history and retains that information for 18 months. 

The benefit of this is displayed on the record in a related list and is even reportable.



Field history data is retained for up to 18 months through your org, and up to 24 months via the API. 

Field history tracking data doesn't count against your Salesforce org's data storage limits.

You can track the field history of custom objects and the following standard objects.


Limitations Of Using Field History Tracking


1.You can only track 20 fields per object.

2.For some fields like Text Area (Long), Text Area (Rich), and Picklist (Multi-Select) and Changes to fields with more than 255 characters are tracked as edited,

 and their old and new values are not saved. 

3.If a trigger causes changes to objects for which the user does not have permission to edit, the change is not tracked. 

 Field history takes into account current user permissions.

 

 


Field Audit Trail : 


Field Audit Trail uses a similar experience but extends those limits to track up to 60 fields and will retain as much as ten years.


Field Audit Trail is a separate add-on feature.It also comes under the Salesforce Shield Product.


Retain Field History for upto 10 years with Field Audit Trail.


Salesforc Shield :


To protect the application data and maintain the customer trust, Salesforce Introduced Salesforce Shield. Salesforce Shield offers a powerful set of tools 

for any customer who needs an extra level of control to meet internal or regulatory compliance requirements.


Salesforce Shield is made up of three components :


1.Platform Encryption

2.Event Monitoring

3.Field Audit Trail


Platform Encryption :


Platform Encryption allows you to encrypt fields, files, and attachments stored in the Salesforce1 Platform. In contrast to Classic Encryption, 

which uses a custom field type in the Salesforce data model, Platform Encryption allows you to encrypt several standard fields and 

custom field types using metadata while preserving functionality necessary to perform common business tasks in Salesforce.


Note : Natively Encrypt sensitive data


Event Monitoring:


Event monitoring lets you to easily see what data users are accessing, from what IP address, and the actions done to that data. 

For Example API calls, user logins, users who are running reports, exporting reports, downloading files, and etc.


Note : Detailed data & Monitoring


Field Audit Trail :


It is used to track information about who, when and what data has changed. Current data retention period is 18 months and we can track up to 20 fields only,

but by upgrading to Field Audit Trail with History tracking, we can track up to 60 fields and can retain data for 10 years.


Note : Prevents Data loss


Different AJAX action tags in visualforce page

 1.apex:actionFunction

2.apex:actionPoller

3.apex:actionRegion

4.apex:actionStatus

5.apex:actionSupport


ActionRegion :


ActionRegion tells to Force.com server which components shoud be processed.

Whatever inside an ActionRegion is processed by a server when AJAX request is generated

on event such as "KeyPress" or "onClick" etc.


Note : 


Sigifies which components should be processed by the server when an AJAX request is generated.


ActionRegion defines the source components that needs to be processed at the server end when the request is sent. 

(or)


ActionRegion tag defines which componnets of VF page should be processed by Force.com server.


By Components  means, all the visualforce tags like inputField, inputText, outputPanels etc.


 ex :

 

 <apex:pageBlock title="Page block" mode="edit" id="infoblock">


        <apex:pageBlockSection title="Information" columns="1">

          <apex:actionRegion> 

               <apex:inputField value="{!myCustomObject.Opportunity__c}">              

                  <apex:actionSupport event="onchange" action="{!changeOpportunity}" rerender="additionalinfo"/>              

               </apex:inputField>

          </apex:actionRegion>    


          <apex:inputField value="{!myCustomObject.MyRequiredField__c}" required="true" /> 


        </apex:pageBlockSection>



        <apex:pageBlockSection title="Additional Info" columns="1" id="additionalinfo">         

                <apex:inputField value="{!myCustomObject.Field1__c}" />

                <apex:inputField value="{!myCustomObject.Field3__c}" />

                <apex:inputField value="{!myCustomObject.Field3__c}" />

                <apex:inputField value="{!myCustomObject.Field4__c}" />         

        </apex:pageBlockSection>


 </apex:pageBlock>

 

ActionSupport :


ActionSupport adds AJAX support to another component, allowing the component to be refreshed asynchronously by the server 

when a particular event occurs, such as a button click or mouseover.


ex 1: 


Used when we want to perform an action on a perticular eventof any control like onchange of any text box or picklist.


<apex:inputText value="{!dummyString}" >

<apex:actionSupport event="onchange" action="{!actionSupportTest}" reRender="pgBlock, pbSection" />

</apex:inputText> 


ex 2 :

 

 <apex:page controller="exampleCon">

<apex:form>

<apex:outputpanel id="counter">

<apex:outputText value="Click Me!: {!count}"/>

<apex:actionSupport event="onclick"

action="{!incrementCounter}"

rerender="counter" status="counterStatus"/>

</apex:outputpanel>

<apex:actionStatus id="counterStatus"

startText=" (incrementing...)"

stopText=" (done)"/>

</apex:form>

</apex:page>


public class exampleCon {

Integer count = 0;

public PageReference incrementCounter() {

count++;

return null;

}

public Integer getCount() {

return count;

}

}



ActionStatus :


ActionStatus is usually used to show the status of an AJAX Process to which it is related to.


ex:

ActionStatus showing text during the update the page and showing a loading gif.


<apex:page controller="AccountFiltctr">

  <apex:actionStatus id="ProgStatusId" rendered="{!actionstatusrendered}">

             <apex:facet name="start">

             <div class="waitingSearchDiv" id="el_loading" style="background-color: #fbfbfb;height: 100%;opacity:0.65;width:100%;"> 

             <div class="waitingHolder" style="top: 74.2px; width: 91px;">

             <img class="waitingImage" src="/img/loading.gif" title="Please Wait..." />

             <span class="waitingDescription">Please Wait...</span>

             </div>

             </div>

             </apex:facet>                           


           </apex:actionStatus>

<apex:form id="frm">

    <apex:commandButton value="Test" status="ProgStatusId" reRender="pb1"/>

</apex:form>

</apex:page>


ActionFunction :


ActionFunction is a component, which provides support for invoking controller action 

methods directly from javascript code with the use of AJAX request.


ex :


<apex:page controller="MyController">

    <apex:form >

        <apex:actionFunction name="getAccountMappedFunction" action="{!getAccount}" oncomplete="doOnComplete('{!MyAccount.Name}');">

            <apex:param name="accountName" value="GenePoint"/>

        </apex:actionFunction>

    </apex:form>


    <script >

        getAccountMappedFunction();


        function doOnComplete(name)

        {

            alert(name);

        }

    </script >

</apex:page>


public with sharing class MyController

{

    public Account MyAccount {get; set;}


    public PageReference getAccount()

    {

        String accountName = Apexpages.currentPage().getParameters().get('accountName');

        MyAccount = [SELECT Name FROM Account WHERE Name = :accountName];

        return null;

    }

}


Note :


Allows for controller methods to be called directly from javascript.

Must be encapsulated in <apex:form> tags.


ActionPoller :


ActionPoller sends the AJAX request to the server periodically according to specified time interval.


Note :

A timer that sends an AJAX request to the server according to a time interval that you specify. 

Each request can result in a full or partial page update.




ex :


<apex:page controller="exampleCon" sidebar="false">

 <apex:form >

        <apex:outputText value="Watch the changing fruit: {!count} : {!currentFruit}" id="counter"/>

        <apex:actionPoller action="{!incrementCounter}" reRender="counter" interval="6"/>

    </apex:form>

</apex:page>


public class exampleCon {


private List<String> fruitList = new List<String>();

    

    Integer count = 0;

    

    // Fruit name to be displayed

    public String currentFruit {

        get{

            currentFruit = fruitList[count];

            return currentFruit; 

        } 

        set;

    }

    

    // Increment the counter value

    public PageReference incrementCounter() {

        if(count< 6) {

        count++;    

        } else {

            count = 0; 

        }

        

        return null;

    }

    

    

    public Integer getCount() {

        return count;

    }

    

    // Constructor define the list of fruits

    public exampleCon () {

        fruitList.add('Mango');

        fruitList.add('Apple');

        fruitList.add('Pineapple');

        fruitList.add('Banana');

        fruitList.add('Orange');

        fruitList.add('Grape');

        fruitList.add('Peach');

    }


}


Inbound Email Services in Salesforce

 Email Services are automated processes that use Apex classes to process the contents,

headers and attachments of inbound emails.


ex :

You can create an email service that automatically creates contact records based on contact information in messages.

Each eamil service can have one or more email service addresses that can receive messages for processing.


ex :


global class CreateTaskEmailExample implements Messaging.InboundEmailHandler {

 

  global Messaging.InboundEmailResult  handleInboundEmail(Messaging.inboundEmail email, Messaging.InboundEnvelope env) {

  

  // Create an InboundEmailResult object for returning the result of the 

    // Apex Email Service

    Messaging.InboundEmailResult result = new Messaging.InboundEmailResult();

  

    String myPlainText= '';

    

    // Store the email plain text into the local variable 

    myPlainText = email.plainTextBody;

   

    // Create a new Task object 

    Task[] newTask = new Task[0];

   

    // Try to look up any contacts based on the from email address.

    // If there are more than one contacts with the same email address,

    // an exception will be thrown and the catch statement block will be executed.

    try 

    {

      Contact vCon = [SELECT Id, Name, Email FROM Contact WHERE Email = :email.fromAddress   LIMIT 1];

      

      // Add a new Task to the contact record we just found above.

      newTask.add(new Task(Description =  myPlainText,

           Priority = 'Normal',

           Status = 'Inbound Email',

           Subject = email.subject,

           IsReminderSet = true,

           ReminderDateTime = System.now()+1,

           WhoId =  vCon.Id));

     

     // Insert the new Task 

     insert newTask;    

     

     System.debug('New Task Object: ' + vCon.name+vCon.id);   

    }

    // If an exception occurs when the query accesses 

    // the contact record, a QueryException is thrown.

    // The exception is written to the Apex debug log.

   catch (QueryException e) {

       System.debug('Query Issue: ' + e);

   }

   

   // Set the result to true. No need to send an email back to the user 

   // with an error message

   result.success = true;

   

   // Return the result for the Apex Email Service

   return result;

   }

   

}


ex 2 :


global class ProcessJobApplicantEmail implements Messaging.InboundEmailHandler {


  global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email,

    Messaging.InboundEnvelope envelope) {


    Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();


    Contact contact = new Contact();

    contact.FirstName = email.fromname.substring(0,email.fromname.indexOf(' '));

    contact.LastName = email.fromname.substring(email.fromname.indexOf(' '));

    contact.Email = envelope.fromAddress;

    insert contact;


    System.debug('====> Created contact '+contact.Id);


    if (email.binaryAttachments != null && email.binaryAttachments.size() > 0) {

      for (integer i = 0 ; i < email.binaryAttachments.size() ; i++) {

        Attachment attachment = new Attachment();

        // attach to the newly created contact record

        attachment.ParentId = contact.Id;

        attachment.Name = email.binaryAttachments[i].filename;

        attachment.Body = email.binaryAttachments[i].body;

        insert attachment;

      }

    }


    return result;


  }


}


Saturday, 29 May 2021

Exceptions that Can’t be Caught in apex

 These below exceptions are uncatchable, catch blocks, as well as finally blocks if any,

aren't executed.


1.System.LimitException

2.System.assert

3.license exceptions


Throws runtime exceptions if a governor limit has been exceeded, such as when the maximum number of SOQL queries issued has been exceeded.

Other examples are exceptions thrown when assertion statements fail (through System.assertmethods) or license exceptions.


Friday, 28 May 2021

Salesforce Debug Logs

 Salesforce provides debug logs to track the events occurring in a Salesforce organization. 

It contains information about the transactions such as whether it was successful and how much time it took to complete the transaction. 

Debug logs are generated based on the ‘User Trace Flag’ in Debug Logs. Debug logs are generated by the system when a user performs a transaction.


1.Debug logs are used to track the events information that occurs in your org.

2.Debugs log, will only be generated when you have an active flag turned on for the logged in user.


Debug Log Category :


Information is logged based on the category called Log Category. Below are the different log categories.


Database     : Includes information about database activity, including every data manipulation language (DML) statement or inline SOQL or SOSL query.

Workflow     : Includes information for workflow rules, flows, and processes, such as the rule name and the actions taken.

Validation     : Includes information about validation rules, such as the name of the rule and whether the rule evaluated true or false.

Callout         : Includes the request-response XML that the server is sending and receiving from an external web service. Useful when debugging issues related to using Lightning Platform web service API calls or troubleshooting user access to external objects via Salesforce Connect.

Apex Code     : Includes information about Apex code. Can include information such as log messages generated by DML statements, inline SOQL or SOSL queries, the start and completion of any triggers, and the start and completion of any test method.

Apex Profiling  : Includes cumulative profiling information, such as the limits for your namespace and the number of emails sent.

Visualforce     : Includes information about Visualforce events, including serialization and deserialization of the view state or the evaluation of a formula field in a Visualforce page.

System         : Includes information about calls to all system methods such as the System.debug method.


What is a Trace Flag?


Trace flag are used to filter the log. Each trace flag includes a debug level, start time, end time, and log type. 

The trace flag’s log type specifies the entity you’re tracing


Salesforce Debug Log Levels :


you can also specify one of the following logging levels.


1.NONE

2.ERROR

3.WARN

4.INFO

5.DEBUG

6.FINE

7.FINER

8.FINEST


Log level determines the amount of information logged when an event is triggered. There are different log levels for each log category. 

The levels are listed below from lowest to highest.



Error, Warn, Info   : Includes error, warning, and information messages.

Debug                   : Includes lower-level messages, and messages generated by calls to the System.debug method.

Fine, Finer           : Includes log messages generated by calls to the System.debug method, every DML statement or inline SOQL or SOSL query, and the entrance and exit of every user-defined method. In addition, the end of the debug log contains overall profiling information for the portions of the request that used the most resources. These resources include SOQL and SOSL statements, DML operations, and Apex method invocations.

Finest               : Includes all messages generated by the Fine or Finer log levels, as well additional information on Apex scripts, including the following.


what do you do when your debug session reaches 2MB?


There are only a few options


1.Reduce the log level details for the user you're debugging.

2.Trace flag the noisiest classes into submission.


Trace Flags :


In order to generate debug logs, we need to set trace flags.

Trace Flags can be set for Apex Classes ,Triggers and Users.


Limitations of a Debug Log :


1.Each debug log must be 5 MB or smaller. Debug logs that are larger than 5 MB are reduced in size by removing older log lines, such as log lines for earlier System.debug statements. The log lines can be removed from any location, not just the start of the debug log.

2.System debug logs are retained for 24 hours. Monitoring debug logs are retained for seven days.

3.Trace flag will be disabled when a debug log more than 250 MB is generated in a 15-minute window. 

An email will be sent to the user who last modified the trace flag so that, they can re-enable it.

4.When your org accumulates more than 250 MB of debug logs, we prevent users in the org from adding or editing trace flags. 

To add or edit trace flags so that you can generate more logs after you reach the limit, delete some debug logs.

Thursday, 27 May 2021

SOQL Performance Tips and Tricks

SOQL is a tool that lets you access records in your Salesforce database.

when you don't write good SOQL queries you're going to hit the governor limit of non selective query.


Query Error : 'System.QueryException : Non-selective query against large object type' 


System.QueryException :


Non-selective query against large object type(more than 100000 rows).

Consider an indexed filter or contact salesforce.com about custom indexing.

Even if a field is indexed a filter might still not be selective when :

1.The filter value includes null(for instance binding with that list that contains null).

2.Data skew exists whereby the number of matching rows is very large

(for instance,filtering for a particular foreign key value that occurs many times ).


To avoid this error,ensure that the SOQL query is selective.


What is a selective query?


A selective query is a query that leverages indexes in filters to avoid full table scans 

and to reduce the number of records in your result set below the selectivity threshold.


Selective SOQL Query Criteria :


1.A query is selective when one of the query filters is on an indexed field.

2.The query filter reduces the resulting number of rows below a system-defined threshold. 

3.The performance of the SOQL query improves when two or more filters used in the 

WHERE clause meet the mentioned conditions.

4. For a standard index, the threshold is 30 percent of the first million targeted records and 15 percent of all records after that first million. 

In addition, the selectivity threshold for a standard index maxes out at 1 million total targeted records

5.For a custom index,the selectivity threshold is 10% of the first million records and less than 5% of the records 

after the first million records,up to a maximum of 3,33,333 records.

6.In some circumstances, for example with a query filter that is an indexed standard field, 

the threshold can be higher.Also, the selectivity threshold is subject to change.

7.If the filter exceeds the threshold, it won’t be considered for optimization.

8.If the filter doesn’t exceed the threshold, this filter is selective, and the query optimizer will consider it for optimization.


How to know if filter is index?


Primary key present on each object (Id, Name, OwnerId), a foreign key (CreatedById, LastModifiedById, lookup, master-detail relationship), 

and an audit field (CreatedDate, SystemModstamp) are index fields.


If parameter value of index field is null in SOQL, then it won’t be considered as an index field. 

Custom fields will have an index if they have been marked as Unique or External Id. 

Custom index can be enabled by contacting Salesforce.com on non-index fields.


Note :

Salesforce has various rules around when an index will be used or not based on its selectivity.

An indexed field will not be used under the following conditions:

1.Not In (….)

2.Like with wildcards "%"

3.!=

4.Excludes for picklist values

5.Not Like

6.Comparison Operators Paired With Text Fields. Text_Field <, >, <=, >=


Note :


When your filter uses != or NOT which includes using NOT EQUALS/CONTAINS for reports, even if the field is indexed

the Force.com query optimizer can’t use the index to drive the query. For better performance, filter using = or IN,

and the reciprocal values.



ex : 


1.When Index field is used in SOQL properly


Select id, name from Account where Account_Code__c =’ProQuest’


Analysis :


You can observe the cost of SOQL. It is 0.0000970 which is very low and will be best to use.


2.When non Indexed field is used in SOQL


Select id, name from Account where Mobile_Phone__c = ‘00000’


Analysis


You can observe the cost of SOQL. It is 0.84 which is much higher. 

Although there are only 50 records found which have this phone no. It is not ideal to use this SOQL.



3.Passing null value in parameters


select id from Opportunity where Booking_External_Id__c IN (‘B10098’,null)


Analysis


This is the common problem what developers do. 

Although index field is used but it can’t optimize SOQL because null is present in a parameter. 

Query optimizer will do Table scan instead of applying index



Advantage of Selective Filters :


1.Reduces the number of records in your result set.

2.Leverages indexes.

3.Avoids full table scans.


Note :

1.Selectivity thresholds determine if an index is considered.

2.Not Equals filters will not leverage indexes.

3.Be careful filtering on Null.

4.And conditions involve an INTERSECTION of indexes.

5.OR conditions involve an ADDITION of indexes.

6.ORDER BY with a LIMIT on an index can make up for non-selective filters.


Note :

1.Query Performance improves with indexes.

2.Use selective filters to reduces result set.

3.Query Optimizer chooses the best table/index to drive a query 

4.Skinny Tables may help when indexing is exhausted.


Skinny Table :


1.Contact Salesforce to enable it.

2.Can contain only 100 fields.

3.Change in field type used in Skinny table would make it invalid.

4.Skinny Table is not copied to sandbox after refresh (Except Full Copy Sandbox).

5.Useful for read operations.

6.Cannot contain fields from other object/Parent.



Selective Query Definition :


Selectivity is determined by the index state of the query filter conditions 

and the number of records the filter returns (Selectivity) versus the object total records.

The thresholds below show the difference between the selectivity calculation for a standard index versus a custom index.


Selectivity Thresholds :


Standard Index - 30% (first 1M) then 15%

Custom Index   - 10% (first 1m) then 5%


Unary filter :


ex : SELECT Name FROM Account where IndexedField__c='ABC'


With a custom index on IndexedField__c the filter must return <10% of the total records

in the object to be considered selective - up to the first 1 million records from that point the threshold drops to 5%.


Multiple filters AND (exclusively):


ex : SELECT Name FROM Account WHERE IndexedField__c='ABC' AND SecondIndexedField__c='123'


The Query Optimiser will set the leading operation on the basis of lowest cost.

if no filters are selective a table  scan is performed.


If all filters have an index then a Composite Index Join optimization can be applied.


In this case each filter must be less than 2x(two-times) the selectivity threshold.

All filters combined must be less than selectivity threshold.


If all filter fields are standard then use the standard index selectivity threshold-otherwise use custom index selectivity threshold.


Multiple filters OR ( at least one) :


ex : SELECT Name FROM Account WHERE IndexedField__c='ABC' OR SecondIndexedField__c='123'


Selective AND filter indexes cloud be set as the Leading Operation - if none exist,then a table scan occurs

unless all filters have an index then a Composite Index Union optimisation becomes possible.


In this case each filter must be less than selectivity threshold.

All filters combined must be less than selectivity threshold.


If all fields are standard then use the standard index selectivity threshold-otherwise use the custom index selectivity threshold.


Note : When using OR condition, all filters must be indexed and under the 10% threshold.


Parent Field Filter :


 ex : SELECT Name FROM Contact WHERE IndexedField__c=’ABC’ and Account.IndexedField__c=’ABC’


Where parent object fields are referenced in a filter, each filter index is individually and the lowest cost option selected as the leading operation.


Note, the parent field is not indexed on the queried object, so Account.Id can incur a table scan on Opportunity 

whereas AccountId may allow the standard index to become the leading operation.


Evaluate the Selectivity :

==========================


In order to evaluate the selectivity of a given query – the following 2 approaches can be applied.


1.REST API Query Resource Feedback Parameter

2.Developer Console Query Plan


Query Resource Feedback Parameter :


The Force.com REST API exposes a Query resource that accepts an explain parameter which can set with a SOQL query, List View Id or Report Id. 

The results show the options considered by the Query Optimiser and the lowest cost option (leading operation) taken. 

A relative cost value of less than 1 indicates a selective query, anything higher indicates non-selective


ex :


/services/data/v52.0/query/?explain=

SELECT+Name+FROM+Merchandise__c+WHERE+CreatedDate+=+TODAY+AND+Price__c+>+10.0


/services/data/v52.0/query/?explain=00Ow0000007lLgO



Query Plan in Salesforce :


The Query Plan tool shows the cost of Salesforce executing a given SOQL query given the database 

statistics know at that time.


The new Query Plan tool in the Developer Console can help speed up SOQL queries done over large volumes. 

Use the Query Plan tool to optimize and speed up queries done over large volumes.


The Query Plan tool will show a list of available plans that our Query Optimizer can utilize for the query provided 

and will be arranged by cost ascending. Each Plan will contain information on Cardinality, Operation Type, Cost, sObject Type, and more. 

Each plan has a “Leading Operation Type”, for example, Field Index or Full Table Scan. 

The plan with the lowest cost is the plan that is used for driving the query execution.


The Query Plan window displays all query operations and the cost of each.


1.Cardinality – The estimated number of records that will be returned by the query.

2.Fields – The indexed field(s) used by the Query Optimizer. If the leading operation type is Index, the fields value is Index. 

Otherwise, the fields value is null.

3.Leading Operation Type – The primary operation type that Salesforce will use to optimize the query.

 a.Index – The query will use an index on the queried object.

 b.Sharing – The query will use an index based on the sharing rules associated with the user who is executing the query. 

  If there are sharing rules that limit which records that user can access, Salesforce can use those rules to optimize the query.

 c.TableScan – The query will scan all records for the queried object.

 d.Other – The query will use optimizations internal to Salesforce.

4.Cost – The cost of the query compared to the Force.com Query Optimizer’s selectivity threshold. 

Values above 1 mean that the query won’t be selective.

5.SObject Cardinality – The approximate record count for the queried object.

6.Sobject Type - The name of the query object.


How Query plan calculates cost?

Each plan has its own cost value. The cost value is derived from the latest gathered database (DB) statistics on the table and values. 

The plan with the lowest cost will be the plan used. If the Cost is above 1, it means that the query won’t be selective. 

There are below reasons for not showing index fields in Query plan.


1.index will never be used when comparisons are being done with an operator like “NOT EQUAL TO”

2.index will never be used when comparisons are being done with a null value like “Name = ””

3.Leading ‘%’ wildcards are inefficient operators that also make filter conditions non-selective

4.When using an OR comparison, all filters must be indexed for optimized results.


Note :

Don't forget about deleted records in your recycle bin.


Did you know that deleted records can effect your query performance.


2 ways to solve this problem :


1.Add isDeleted = false to your queries.This field is available on all standard and custom objects.

2.Empty the recycle bin.


Note :


MAX # of Master-Detail Relationship Fields, per Object (Standard or Custom) -- 2

MAX # of total Relationship Fields (Master-Detail + Lookup), per Object (Standard or Custom) -- 40


Relationship Query :


1.Child to parent


SELECT Id,Name,Broker__c,Broker__r.Id,Broker__r.Region__c FROM Property__c WHERE Status__c='Available'


2.Parent to Child 


SELECT Id,Name,Region__c,(SELECT Id,Name,Status__c FROM Properties__r) FROM Broker__c WHERE Region__c='North'


Querying for Intersection :


SELECT Id,Name,Broker__r.Name,Broker__r.Email__c FROM Property__c WHERE Broker__r.Region__c='North'


Aggregate Queries :


SELECT City__c,COUNT(Id) houseCount,MAX(Price__c),AVG(Price__c) avgValue FROM Property__c GROUP BY City__c