Exception: QUERY_TIMEOUT Your query request was running for too long
Take a look at Working with Very Large SOQL Queries. Querying for field != null
will hurt your performance.
Typically, a custom index isn’t used in these cases.
- The queried values exceed the system-defined threshold.
- The filter operator is a negative operator such as NOT EQUAL TO (or !=), NOT CONTAINS, and NOT STARTS WITH.
- The CONTAINS operator is used in the filter, and the number of rows to be scanned exceeds 333,333. The CONTAINS operator requires a full scan of the index. This threshold is subject to change.
- You’re comparing with an empty value (Name != '').
I don't have 25M records to test against, but I believe you might have better luck creating a formula along the lines of Is_Missing_Account__c
:
AND(ISNULL(AccountId), ISNULL(accountrefId__c))
If the above does not work, you can use an Apex Trigger
, a Workflow Rule
, or Process Builder
to sync an analogous checkbox. You will need to run a back-fill batch, but once you do that, you can just filter on this custom checkbox instead and it should speed things up.
Another option is to populate an Account_Count__c
(formula or otherwise) number field with 0, 1, or 2 depending on these parent relationships. Then filter Account_Count__c = 2
.
Text field indexes do not index "null" values. In other words, the database can't reduce the cost for a query that filters for a null value in a text field. If most of your table has values, but a few do not, this can easily cause the query timeout exception you're receiving. Odds are, you'll probably need to resort to more drastic measures, such as perhaps manually performing a full table scan:
public class MyBatch implements Database.Batchable<SObject> {
public Database.QueryLocator start(Database.BatchableContext context) {
return Database.getQueryLocator([SELECT Id FROM Contact]);
}
public void execute(Database.BatchableContext context, Contact[] scope) {
Contact[] records = [SELECT Id, AccountId, AccountRefId__c FROM Contact
WHERE Id IN :scope AND
(AccountId = NULL OR AccountRefId__c = NULL)];
// Now process these records.
}
public void finish(Database.BatchableContext context) {
}
}
This will work for up to 50,000,000 rows, after which point you'd have to also implement batch chaining with large limits, etc.