3

I have a little problem. We have 2000 records of type "Immobenutzer". So now I am facing the problem of governor limits. I need to log the logintimes for each user. So for each user there has to be a new query to get the amount of login times. Now I have no clue how to avoid these limits anymore. I just know about the trick I used in the first execute method, by using not a for loop and then querying inside, but using the query as statement for both lists and single record. Can someone help?

global class RealtyUserLoginDays implements Schedulable
{
    // Execute method
    global void execute(SchedulableContext SC) 
    {
        for(List<Realty_User__c> userList:[SELECT Id, CreatedDate, Tage_ohne_Login__c, Tage_mit_aufeinander_folgendem_Login__c FROM Realty_User__c WHERE Active__c = TRUE])
        {
            for(Realty_User__c user:userList)
            {
                Integer consecutiveLogins = getConsecutiveLogins(user.Id);
                if(consecutiveLogins > 0)
                {
                    user.Tage_mit_aufeinander_folgendem_Login__c = consecutiveLogins;
                    user.Tage_ohne_Login__c = 0;
                }
                else
                {
                    Integer daysWithoutLogin = getDaysWithoutLogin(user.Id, user.CreatedDate.date());
                    user.Tage_ohne_Login__c = daysWithoutLogin;
                    user.Tage_mit_aufeinander_folgendem_Login__c = 0;
                }
            }
            if(userList.size() > 0)
            {
                update userList;
            }
        }
    }

    public static Integer getConsecutiveLogins(Id userId)
    {
        Set<Date> loginTimes = new Set<Date>();
        for (Loginzeiten_Maklerportal__c login : [SELECT Loginzeit__c FROM Loginzeiten_Maklerportal__c WHERE Immo_Benutzer__c = :userId])
        {
            loginTimes.add(login.Loginzeit__c.date());
        }
        Integer consecutiveLogins = 0;
        while (loginTimes.contains(Date.today().addDays(-consecutiveLogins)))
        {
            consecutiveLogins++;
        }
        return consecutiveLogins;
    }

    public static Integer getDaysWithoutLogin(Id userId, Date createdDate)
    {
        Date today = Date.today();
        try
        {
            Loginzeiten_Maklerportal__c login = [SELECT Loginzeit__c FROM Loginzeiten_Maklerportal__c WHERE Immo_Benutzer__c = :userId ORDER BY Loginzeit__c DESC LIMIT 1];
            Date loginZeit = login.Loginzeit__c.date();
            Integer daysWithoutLogin = loginZeit.daysBetween(today); 
            return daysWithoutLogin;
        }
        catch(Exception e)
        {
            Integer daysWithoutLogin = createdDate.daysBetween(today);
            return daysWithoutLogin;
        }    
    }
}

2 Answers 2

7

You need to bulkify your methods, and the pattern for doing this is to gather the userids of all the users you are interested in and make one query, rather than issue one query per user.

So in your example, in the execute method you could collect a list of user ids that are relevant in this context

    Set<ID> userIDs = new Set<ID>();
    for(Realty_User__c user:userList)
    {
         userIDs.add(user.id);
    }

Then in the methods you can do something like this

Map<ID, List<Loginzeiten_Maklerportal__c> userToLoginzeitinMap = new Map<ID, List<Loginzeiten_Maklerportal__c>);
for (Loginzeiten_Maklerportal__c lm : [SELECT Loginzeit__c, Immo_Benutzer__c  FROM Loginzeiten_Maklerportal__c WHERE Immo_Benutzer__c in :userIDs]) {
     List<Loginzeiten_Maklerportal__c> lmList = userToLoginzeitinMap.get(lm.Immo_Benutzer__c);
     if (lmList == null) lmList = new List<Loginzeiten_Maklerportal__c>();
     lmList.add(lm);
     userToLoginzeitinMap.put(lm.Immo_Benutzer__c, lmList);
}

The end result is that by only using one SOQL query you have built a map and when you do userToLoginzeitinMap.get(userID) it returns the query results for that user. Hope this pattern is clear and helps you out.

6
  • Hey, I guess this will work, but one question: I don't just need the Id for the users, I also need 3 other fields. How would I achieve this? Commented Jul 6, 2015 at 12:23
  • Can you explain the problem you have? In the example code I gave it shows selecting two fields for each user. You can select any extra fields in the same manner i.e. SELECT Loginzeit__c, Immo_Benutzer__c, Other_field1__c, Other_Field2__c ....
    – Doug B
    Commented Jul 6, 2015 at 12:48
  • I mean I need fields of the user. If you look into my example, when there is no "Loginzeiten_Maklerportal__c" object, I need to check the created date of the user to write today - created days into the field "Days without login". So I need not only the ID to each user, but also the createdDate to each user Commented Jul 6, 2015 at 13:00
  • *loginzeiten_maklerportal__c record Commented Jul 6, 2015 at 15:46
  • In your execute method you can continue to use the code that you have now, selecting several fields from user. In addition, you build the set of userIds which is what you use in the other queries instead of doing one query per user.
    – Doug B
    Commented Jul 6, 2015 at 15:48
0

If you wnat to overcome witj this problem then there are different ways but this is not sure that it will help every where.

1: Use Maps to fill your data.

2: Rather then using list to query in for loop use direct query like :

for(test t : [select xyz from test])

3. If you want to show these data on your page than at a time you can query only for 50000 but you can increse this limit by using read only attribute on the page.

1
  • I have learned in past, that your 2. suggestion would exceed the governor limits pretty fast, because if there are more than 200 records, than it will fail... so you need to use the list for loop Commented Jul 6, 2015 at 12:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .