Inner Joins in SOQL With Relationship - salesforce

I need a help in SOQL. I am new to this, so please bear with me.
I have to do a downwards traverse in SOQL.
SELECT Id, (SELECT Name from Contacts WHERE CreatedDate > YESTERDAY AND LastModifiedDate > YESTERDAY) from Account where CreatedDate > YESTERDAY AND LastModifiedDate > YESTERDAY
I want to get all records from Account and Contact where created date or last modified date is within a certain range. I want records where there are no changes in Account Object but changes are there in records in Contact Object.
But this query will not fetch any records if there are any changes in only Contact and no change in Account. How can I possibly do that.
Please help!

You're doing a Parent-Child SOQL query. The Contact subquery only matches Contacts associated with Accounts that match the primary query's filters.
You'll need to run one query on Account and a separate, non-relationship-based query on Contact.

Probably the following answer might help someone.
Query:
SELECT Id, Name, LastModifiedDate,(SELECT Name,LastModifiedDate from Contacts WHERE LastModifiedDate >= YESTERDAY) from Account where LastModifiedDate < YESTERDAY AND Id IN (SELECT AccountId FROM Contact WHERE LastModifiedDate >= YESTERDAY).
Explanation:
Assuming the certain range of date is yesterday, we want to retrieve all accounts that have contacts which are modified yesterday and after yesterday but the accounts should not have been modified in the above specified range.
The above query is parent-to-child query. In the sub-query we are checking for contacts that have been modified yesterday and after that. In the outer query we are checking that accounts that might have been modified before yesterday.
Now if we stop here and do not include the IN clause the results fetched will be a outer join that is along with required results it fetches accounts that have been modified before yesterday even if they do not have a contact that has been modified yesterday or after that. See attached pic. Outer Join results
So We have to include another check in the outer query ensuring that accounts that have contacts that have satisfied the specified date range are only returned and this is ensured using IN clause. See attached pic. Inner Join results.
Please mark this as useful if it helps.
Thanks

Related

SOQL - Count of records where the parent hasn't had child records in 1 year

I am trying to create a SOQL query (and later a report) in Salesforce that generates the following data:
Count of Child records grouped by Parent records where the Parent does not have child records created in the past year.
I tried this first; however, salesforce returned an error stating 'Nesting of semi join sub-selects is not supported'.
SELECT Id, Name, Training_Course__c
FROM Training_Record__c
WHERE Training_Course__c IN (
SELECT Id
FROM Training_Course__c
WHERE Id NOT IN (
SELECT Training_Course__c
FROM Training_Record__c
WHERE CreatedDate != Last_n_Days:365
)
)
The requirements are to use a single query to obtain the data requested without forcing them to run two reports and use Excel to get the data. I'm not sure if that's possible given Salesforce's constraints.
Is this possible in SOQL? If so, what can I do differently?
This is not perfect but a decent start, you'd need to manually call count/size/length on the child records (exact method depends on your client application's language). In a SF report you can group by parent Id and call it a day. Read about "cross filters" in SF reports.
SELECT Id, Name,
(SELECT Id FROM Opportunities)
FROM Account
WHERE Id NOT IN (SELECT AccountId FROM Opportunity WHERE CloseDate = LAST_N_DAYS:365)
LIMIT 100
This will not compile:
SELECT AccountId, COUNT(Id)
FROM Opportunity
WHERE AccountId NOT IN (SELECT AccountId FROM Opportunity WHERE CloseDate = LAST_N_DAYS:365)
Error: The inner and outer selects should not be on the same object type
but if you really need something like that you could experiment, do it in 2 steps. Create helper field on account, tick the checkbox for eligible accs (with some nightly batch job maybe), then query based on that.
(normally you could do it with rollup summary field from opps to account but rollups have to be based on solid data, "deterministic". "LAST 365 DAYS", "TODAY" etc can't be used as rollup criteria)

Why doesn't this "not in" clause do what I expect?

I'm writing what I think should be a simple query using the "not in" operator, and it's not doing what I expect.
Background:
I have two tables, Contact and Company.
Contact includes columns ContactID (person's identity) and CompanyID (which company they work for)
CompanyID values are expected to be equivalent to the CompanyIDs in the Company table
I want to write a query that checks how many people from the Contact table that have an "invalid" CompanyID (i.e., listed as working for a Company that isn't in the Company table)
I have a working query that does this:
select
count(ContactID)
from
Contact left join Company on Contact.CompanyID = Company.CompanyID
where
Company.CompanyID is null;
This query returns the value 2725538, which I believe to be the correct answer (I've done some simple "show me the top 10 rows" debugging, and it appears to be counting the right rows).
I wrote a second query which I expected to return the same result:
select
count(ContactID)
from
Contact
where
CompanyID not in
(select
CompanyID
from
Company)
However, this query instead returns 0.
To help me debug this, I checked two additional queries.
First, I tried commenting out the WHERE clause, which should give me all of the ContactIDs, regardless of whether they work for an invalid company:
select
count(ContactID)
from
Contact
This query returns 29722995.
Second, I tried removing the NOT from my query, which should give me the inverse of what I'm looking for (i.e., it should count the Contacts who work for valid companies):
select
count(ContactID)
from
Contact
where
CompanyID in
(select
CompanyID
from
Company)
This query returns 26997457.
Notably, these two numbers differ by exactly 2725538, the number returned by the first, working query. This is what I would expect if my second query was working. The total number of Contacts, minus the number of Contacts whose CompanyIDs are in the Company table, should equal the number of Contacts whose CompanyIDs are not in the Company table, shouldn't it?
So, why is the "not in" version of the query returning 0 instead of the correct answer?
the only issue could be of NULL CompanyID. Not In doesn't work with NULLs because of non-comparability of NULL.
try the following:
select
count(ContactID)
from
Contact
where
CompanyID not in
(select
ISNULL(CompanyID,'')
from
Company)
you can see the example in db<>fiddle here.
Please find more details HERE.

Filtering issue with multiple forloop in apex | salesforce

I have a scenario where multiple loopings are causing the system resource error.
I need some help with map of map syntax or coding sample for this requirement.
Requirement is:
Account has 1 or more ReportCard records.
ReportCard has Account and Contact.
Now i need to get the list of ReportCards and filter by 1 per contact and recently created records only.
If ReportCard has 2 records with same contact, include only recently created.
// get list of unique accounts from the set
list<Account> accList = new list<Account >([SELECT Id,Average_of_Pulse_Check_Recommend_Score_N__c,Average_of_Recommend_Score_Lanyon_N__c,Average_of_Touchpoint_Recommend_Score_N__c,Average_of_Touch_Point_Satisfaction_N__c FROM Account WHERE Id in:AccIds]);
list<ReportCard__c> allRCList = new list<ReportCard__c>([SELECT Id,Net_Promoter_text__c,CreatedDate, Contact__c, Account__c, RecordTypeID, Touchpoint_Satisfaction_text__c FROM ReportCard__c WHERE Account__c in:accList Order By Account__c, CreatedDate Desc]);
List<ReportCard__c> rcListbyAccounts = new List<ReportCard__c>();
for(Account acc:accList)
{
Any help would be appreciated.
Thanks
I'm not sure I understand your situation correctly. You've skipped the for loop - I strongly suspect any issues you have there sit in the loop rather than in the queries.
Looks like you should read about using relationship queries (salesforce versions of JOIN in regular database): http://www.salesforce.com/us/developer/docs/soql_sosl/Content/sforce_api_calls_soql_relationships.htm
Pay special attention to subqueries (which behave similar to how a related list behaves on record's detail page).
From what I see I'd say you don't need to query for Accounts at all, or at least not like that. This will work equally well:
SELECT Id,Net_Promoter_text__c,CreatedDate, Contact__c, Account__c, ...
FROM ReportCard__c
WHERE Account__c in:accIds
ORDER BY Account__c, CreatedDate Desc
Now lets attack this:
List of ReportCards and filter by 1 per contact and recently created
records only. If ReportCard has 2 records with same contact, include
only recently created.
I'd reverse it - I'd start the query from Contact level, go down to the related list of Report Cards and pick the latest one. That way it eliminates the issue with duplicate contacts for you. Something like this:
SELECT Id, Name, Email,
(SELECT Id, Net_Promoter_text__c, CreatedDate, Account__c, Account__r.Name, Account__r.Average_of_Pulse_Check_Recommend_Score_N__c
FROM ReportCards__r
WHERE Account__c IN :accIds
ORDER BY CreatedDate DESC
LIMIT 1)
FROM Contact
WHERE AccountId IN :accIds
This goes from Contact "down" to Report Cards (via the relationship name ReportCards__r) and then "up" from Card to Account via Account__r.Name, Account__r.Average_of_Pulse_Check_Recommend_Score_N__c...

A challenge for you SQL lovers out there

I'm new to the working world an am fresh out of varsity. i started working an am creating a few reports via SQL reporting services as part of my training.
Here is quite a challenge that I am stuck at. Please help me finish this query. Here is how it goes!
There are several employees in the employee_table that each have unique identifier known as the emp_id
There is a time_sheet table that consists of several activities AND THE HOURS FOR EACH ONE for the employees and references them via emp_id. Each activity has a TIMESHEET_DATE that corresponds to the day all the activities were submitted(once a month). There are several activities with the same date because all those activities were submitted on the same day.
And there is a leave table that references the employees via emp_id. In the leave table, there is a column for the amount of days they took off and the starting day (Leave_FROM) of the leave.
I must create a parameter where the user inputs the month (easy peasy)...
Now in the report, column 1 must have their name (easy), column 2 must have their totals hours for the specified month (HOURS) and column 3 must show how many days they took leave for that month specified.
It can be tricky, not everybody has a entry in the leavetable, but everybody has got activities in the Time_Sheet table.
Here is what I have gotten so far from a query, but its not really helping me.
Unfortunately, I cannot upload pictures, so here is a link
http://imageshack.com/a/img822/8611/5czv.jpg
Oh yea, my flavor of SQL is SQL Server
You have a few different things you need to attack here.
First is getting information from the employee_table, regardless of what is in the other two tables. To do this, I would left join on both of the tables.
Your second battle is, now since you have multiple rows in your time_sheet table, you are going to get a record for every time_sheet record. That is not what you want. You can fix this by using a SUM Aggregate and a GROUP BY clause.
Next is the issue that you are going to have when nothing exists in leave table and it is returning NULL. If you add an ISNULL(value,0) around your leave table field, it will return 0 when no records exist on that table (for that employee).
Here is what your query should look like (not exactly sure on table/column naming):
I changed the query to use temp tables, so totals are stored separately. Since the temp tables will hold 0 for employees that don't have time/leave, you can do an inner join on your final query. Check this out for more information on temp tables.
SELECT e.emp_id, ISNULL(SUM(ts.Hours),0)[Time]
INTO #TotalTime
FROM employee e
LEFT JOIN time_sheet ts ON e.emp_id = ts.emp_id
GROUP BY e.emp_id
SELECT e.emp_id, ISNULL(SUM(l.days),0) [LeaveTime]
INTO #TotalLeave
FROM employee e
LEFT JOIN leaveTable l ON e.emp_id=l.emp_id
GROUP BY e.emp_id
SELECT e.Emp_Id,Time,LeaveTime FROM Employee e
INNER JOIN #TotalTime t ON e.Emp_Id=t.Emp_Id
INNER JOIN #TotalLeave l ON e.Emp_Id=l.Emp_Id
DROP TABLE #TotalLeave,#TotalTime
Here is the SQL Fiddle
Left join the leave table, if nobody took leave you won't get any results.

Salesforce - Apex - query accounts based on Activity History

Hey Salesforce experts,
I have a question on query account information efficiently. I would like to query accounts based on the updates in an activityHistory object. The problem I'm getting is that all the accounts are being retrieved no matter if there's "complete" activeHistory or not. So, Is there a way I can write this query to retrieve only accounts with activeHistory that has status="complete" and Type_for_reporting='QRC'?
List<Account> AccountsWithActivityHistories = [
SELECT
Id
,Name
,( SELECT
ActivityDate
,ActivityType
,Type_for_Reporting__c
,Description
,CreatedBy.Name
,Status
,WhatId
FROM ActivityHistories
WHERE Status ='complete' and Type_for_Reporting__c = 'QRC'
)
FROM Account
];
You have a WHERE clause on the histories but you still miss one on the Account level.
For example this would return only Accounts that have Contacts:
SELECT Id, Name
FROM Account
WHERE Id IN (SELECT AccountId FROM Contact) // try with NOT IN too
With Activities it's trickier because they don't like to be used in WHERE in that way.
http://www.salesforce.com/us/developer/docs/officetoolkit/Content/sforce_api_calls_soql_select.htm
The following objects are not currently supported in subqueries:
ActivityHistory
Attachments
Event
EventAttendee
Note
OpenActivity
Tags (AccountTag, ContactTag, and all other tag objects)
Task
Additionally the fine print at the bottom of ActivityHistory definition is also a bit discouraging.
The following restrictions on users who don’t have “View All Data” permission help prevent performance issues:
In the main clause of the relationship query, you can reference only
one record. For example, you can’t filter on all records where the
account name starts with ‘A’; instead, you must reference a single
account record.
You can’t use WHERE clauses.
You must specify a limit of 499 or fewer on the number of rows returned in the list.
You must sort on ActivityDate in ascending order and LastModifiedDate in descending order; you can display nulls last. For
example: ORDER BY ActivityDate ASC NULLS LAST, LastModifiedDate DESC.
Looks like you will need multiple queries. Go for Task (or Event, depending for which the custom field is visible), compose a set of AccountIds and then query the Accounts?
Or you can manually filter through list from your original query, copying accounts to helper list:
List<Account> finalResults = new List<Account>();
for(Account a : [SELECT...]){
if(!a.ActivityHistories.isEmpty()){
finalResults.add(a);
}
}

Resources