This could be base of your query for duplicates:
SELECT COUNT(Id), AccountId, OwnerId, ActivityDate
FROM Event
GROUP BY AccountId, OwnerId, ActivityDate
ORDER BY COUNT(Id) DESC
LIMIT 10
Don't run it yet! It's very likely to timeout and I can imagine it having serious performance issues.
This version is bit better (and it returns the earliest Event that matches the conditions):
SELECT MIN(Id) oldest, COUNT(Id), AccountId accId, OwnerId userId, ActivityDate d
FROM Event
WHERE AccountId != null AND ActivityDate = THIS_MONTH
GROUP BY AccountId, OwnerId, ActivityDate
HAVING COUNT(Id) > 1
ORDER BY COUNT(Id) DESC
LIMIT 10
Keep tweaking it till you're happy it can run in reasonable time. Make the WHERE clause selective, maybe increase the HAVING... The MIN(Id)
is an idea for the "winner" record. Maybe you'll decide that newest one should win. Maybe you'll build something that asks the user to decide...
Anyway. Let's say that "earliest" Id will be a winner (come to thing about it, we probably shouldn't count in Id's being sequential...).
So you have List<AggregateResult>
which you can use to make the detailed query for all Events:
// Id oldestEvent = (Id) results[0].get('oldest');
Id accId = (Id) results[0].get('accId'), userId = (Id) results[0].get('userId');
DateTime actDate = (DateTime) results[0].get('d');
Use these to build SELECT ... FROM Event WHERE AccountId = :accId AND ...
(optionally AND Id != oldestEvent
if you want to skip my "winner". Or sort them by CreatedDate / ActivityDate DESC
if you want to be sure the record you want is first in the result set.
The standard merge statement works only on Accounts, Contacts and Leads. I think you'll have to delete them with your own code, deciding which values to keep in the winner, which should just go... Luckily we can't make lookups to Events so at least you don't have to worry about relationships...
You can find all the child relationships with a little Execute Anonymous
:
Schema.DescribeSObjectResult describe = Account.sObjectType.getDescribe();
for (Schema.ChildRelationship child : describe.getChildRelationships())
{
system.debug(
child.getChildSObject() + '.' + child.getField() + ' - ' +
child.getRelationshipName()
);
}
You'll see a bunch of rows like:
AccountContactRole.AccountId - AccountContactRoles
You could then choose which of these you care about (there are a lot you probably do not), and use one SOQL query to grab them:
SELECT
(SELECT Id FROM AccountContactRoles),
(SELECT Id FROM Attributes__r),
(SELECT Id FROM Assets)
FROM Account
WHERE Id = :accountId
This will only consume one query.
Best Answer
It is impossible to query records from different subject types. Therefore the database class implements several methods to delete "specific" records.
Potential solution
I'm not 100% sure but it looks like there is no limit for
Database.getDeleted
method calls.