is SOQL capable of self joins mysql style? - salesforce

Is it possible to do a self join with table aliasing? Lets's say I'm a book shop and I want to get all customers that purchased a book last week AND this week. In MySQL this would look somehow like this:
SELECT Account.id from Opportunity o1, Opportunity o2
WHERE o1.closeDate = Last_WEEK AND o2.closeDate = This_WEEK
AND o1.Account = o2.Account
What would be the equivalent in MySQL? It keeps puzzling me.

You can't compare one Opportunity Account Id with another in SOQL (o1.Account = o2.Account). If you try you will get the message:
Bind variables only allowed in Apex code
If instead you rework the SOQL to use a sub query you will get a different error.
SELECT AccountId from Opportunity o1
where o1.closeDate = LAST_WEEK
AND o1.Account in
(SELECT AccountId from Opportunity o2 WHERE o2.closeDate = THIS_WEEK)
Gives:
The inner and outer selects should not be on the same object type.
You will need to either:
perform two queries to get the required data. You could feed the resulting Account Ids of the first query into the second query as a where clause filter.
retrieve the Account Ids with either close date in one SOQL query and then build up the required sets with code.
Incidentally, the Salesforce StackExchange site is a great place to ask Salesforce specific questions.

Related

Query of records based on relationships

I have an object Contract that has a look-up to another object Indexationtype. I have another object IndexationEntry that has master-detail to Indexationtype. Now I would like to get the value of the percentage field in the IndexationEntry onto Contract based on the yer fields. The year in the IndexationEntry matches Year in Contract. How should I achieve this?
From Contract "up" to IndexationType__c, then "down" to IndexationEntry__c?
If there's no direct link between them it's not going to be pretty. One way would be something like this
SELECT Id, Name,
(SELECT Id, ContractNumber FROM Contracts__r WHERE Year__c = '2021'),
(SELECT Id, Percent__c FROM IndexationEntries__r WHERE Year__c = '2021')
FROM IndexationType__c
You'd have to run it once for each year. Or (since you tagged it Apex) maybe you can prepare the reference data a bit, query Indexation Types + Entries and build something like Map<Id, Map<Integer, IndexationEntry__c>> (1st key is by Indexation Type Id, then by year). Query them, populate the Map, then loop through your contracts and use map.get() to fetch your values.

Query with multiple joins and optional parameters

I have something of a Search App. There are 7 fields (first name, last name, phone, street, city, shop number, credit card number) where user can write parameters and it's gonna find him clients in the database. Everything is working with AND condition, so when first name is 'Andy' and last name is 'Larkin' is only gonna find Andy Larkins etc. User can leave a field empty, that means when first name is 'Andy' then it should find all the Andys etc. Database looks like this:
The 'Relation' table is to connect person and a shop. Person must have 1 address, 1 shop, can have multiple addresses, multiple shops and no credit card/multiple credit cards. Now, I have to handle all the filtering in a single query, I can't check some conditions before and then construct the query another way, I just don't have that option.
When I search by first name or last name it's fast (both in Person table), but when I search by phone number, or credit card number - it's taking a lot of time. There is a lot of data in the database, but still, my query is bad, I'm not really good at writing queries, especially in Oracle. Here's the query:
SELECT
PERSON.personId,
PERSON.firstName,
PERSON.lastName
ADDRESS.street,
ADDRESS.city,
ADDRESS.phoneNumber
FROM
PERSON
LEFT JOIN ADDRESS ON PERSON.personId = ADDRESS.personId,
LEFT JOIN RELATION ON PERSON.personId = RELATION.personId,
LEFT JOIN SHOPS ON RELATION.shopId = SHOPS.shopId
LEFT JOIN CREDITCARDS ON PERSON.personId = CREDITCARDS.personId
WHERE
PERSON.firstName = NVL(?, PERSON.firstName),
PERSON.lastName = NVL(?, PERSON.lastName),
ADDRESS.phoneNumber = NVL(?, ADDRESS.phoneNumber),
ADDRESS.street = NVL(?, ADDRESS.street),
ADDRESS.city = NVL(?, ADDRESS.city),
SHOPS.shopNumber = NVL(?, SHOPS.shopNumber),
CREDITCARDS.creditCardNumber = NVL(?, CREDITCARDS.creditCardNumber);
The parameters that user left empty are passed as NULLS, that's why I use NVL. When I delete all conditions and leave let's say a credit card number, then it's fast, so I guess that means that all the unnecessary condition checking is slowing the query, and I don't really need that condition checking in most cases, it's just there in case a user passes something.
If I would have the option to check for conditions and only then construct a query then I would just add the conditions that are needed, but I don't have that option. I was thinking about adding some 'IFs' in the query, but I'm not sure that's even possible, all I could find was 'IF/CASE WHEN' but couldn't find any examples that apply to my case. I also tried this:
...WHERE (? IS NULL OR (PERSON.firstName = NVL(?, PERSON.firstName))) AND...
That didn't help, and I got tons of duplicated (different only in address or something - person can have multiple addresses) results (even with 'DISTINCT').
It's not homework, that database is huge with lot of other fields, but I simplified it here, there is also a lot of data there. Thanks for help.
A few things to think about here.
Be careful about queries that might not make sense; such as those that query a credit card number and an address. Queries of that nature fall into a fan trap.
Creating referential integrity constraints in the database will allow the optimizer to do join elimination.
It would be much better for the optimizer, if you could build the query "where clause" dynamically, rather than using NVL functions.
A nested select on the shops might improve performance especially considering it's outer joined. The query below should be enough to get you the idea.
Regarding de-duplication- it's hard because you are selecting Id's and 'distinct' won't help. You'd probably have to use the group by syntax and that might slow the query even more.
If sorting can be done on the client it might help with performance. If the amount of data being returned is significant due to fan-out of relational data and group by isn't a good option then creating a stored procedure might be the best option so most the work is done on database and minimal data over the wire.
SELECT
p.personId,
p.firstName,
a.city,
a.phoneNumber,
shop.shopNumber
FROM
PERSON p,
ADDRESS a,
CREDITCARDS c,
(select ss.personId, ss.shopId, ss.shopNumber from shop s, relation r
where s.shopId = r.shopId) as shop
WHERE
p.personId = a.personId AND
p.personId = c.personId AND
p.personId = shop.personId (+)

How to get account and contact of a particular case using soql query in salesforce?

I want to show accounts and contacts data of a particular case in a form. how can i query the same using soql query in salesforce.
I have a custom object named Work_Order__c which is having a master-detail relationship with CASE object.
I am trying to write a below mentioned query, but the inner query is giving error.
select Id,Case__r.CaseNumber,Case__r.Description,Name,
Status__c,Priority__c,Description__c,City__c,Street__c,Zip__c,
(select Case__r.Account.Name, Case__r.Contact.Name from Work_Orders__r)
from Work_Order__c where Id = 'a024B0000025L6G'
The inner query is giving you an error because Work_Orders__r isn't a table. It is syntactic-sugar that gives you the list of all Work_Orders that are associated with a Case.
What you probably want is something like this:
SELECT Id,Name,Status__c,Priority__c,Description__c,City__c, Street__c,Zip__c,
Case__r.CaseNumber, Case__r.Description,
Case__r.Contact.FirstName,Case__r.Contact.LastName,Case__r.Contact.<Other_Contact_Fields>,
Case__r.Account.Name, Case__r.Account.<Other_Account_Fields>
FROM Work_Order__c
WHERE Id = 'a024B0000025L6G';
My other recommendations is to avoid hard coding your Id.

How to query all task which are having relatedto(whatId) as Account or Contact

I am using an soql query to get tasks. My requirement is to get all tasks which are related account or contact object. Also few other fields inside Account or contact object whichever the object related. Is there a simple way instead of writing multiple queries.
Please provide more specific information next time. Generally speaking you can refer to a parent object by reference name followed with a dot. Here is an example
Select Account.Name, AccountId From Task Where Account.Name = 'John'
Here Account is the name of the reference (from task) and AccountId is the referencing field.
Your question is a little unclear. Are you looking for all TASKS related to any ACCOUNT or CONTACT or related to specific ACCOUNTS or CONTACTS?
If the former, try
SELECT Id, Subject,
FROM TASK
WHERE
What.Type = 'Account' OR
Who.Type = 'Contact'
if the latter, use the IN :list syntax already suggested by Moti. The What.Type is useful, as opposed to AccountId!=null, because it will not return TASKS associated with, for example, OPPORTUNITIES (if you want that behavior, use AccountId!=null and maybe drop the Who.Type if you associate all CONTACTS with OPPORTUNITIES, as it would be redundant).
In either case, your issue will be pulling specific data from CONTACT whos, as polymorphic fields only allow access to a limited number of fields. Can't seem to find that list right now. I don't believe old SOQL supports the kind of the syntax to do that in one query--that's why SOQL polymorphism made such a bang--though I could be wrong.
Now, if you have SOQL TYPEOF available to you, you should be able to do something more interesting like:
SELECT Id, Subject,
TYPEOF What
WHEN Account THEN AccountNumber
END,
TYPEOF Who
WHEN Contact THEN FirstName
END
FROM Task
WHERE
What.Type = 'Account' OR
Who.Type = 'Contact'
How about
select {column list] from task where parentid in (select id from account where ...) or parentid in (select id from contact where ...)
Alternatively, if you're inside Apex and already have the contact or account ids in a list (we'll use idList), you can use:
select {column list] from task where parentid in :idList

Ordering and Limiting A Subquery In Salesforce SOQL

I am attempting to retrieve the owner of a case, based on a partial match, where we choose the most recent case that matches the partial match.
This is the query I am attempting:
SELECT User.CustomField__c
FROM User
WHERE User.Id IN (
SELECT OwnerId
FROM Case
WHERE Case.CaseNumber LIKE '%1026'
ORDER BY Case.CreatedDate DESC LIMIT 1)
The following query works on its own, but doesn't seem happy as part of the subquery:
SELECT OwnerId
FROM Case
WHERE Case.CaseNumber LIKE '%1026'
ORDER BY Case.CreatedDate DESC LIMIT 1
Equally, if I drop the ORDER BY and LIMIT it works:
SELECT User.NVMContactWorld__NVM_Agent_Id__c
FROM User
WHERE User.Id IN (
SELECT OwnerId FROM Case
WHERE Case.CaseNumber LIKE '%1026')
Are order / limit queries not allowed in a SOQL subquery?
Just to clarify this issue, the scenario I am dealing with looks like this...
A Salesforce organisation can configure the "display format" for Case Numbers. If they select "4" digits, you get case numbers like:
0001
0125
1234
33456
It is possible to reconfigure your case numbers to get the following case numbers as well as the case numbers above...
000001
001234
033456
I didn't want people to be confused by the LIKE statement, the issue is that 001234 and 1234 are different cases, so if a customer supplies 1234 and I find two records, I want to start off assuming that they are the most recent case.
So either consider the LIKE statement or an IN statement that contains ('001234', '1234')
There is no documentation I could find that specifies that LIMIT and/or ORDER BY do not work with subqueries, but I ran into the same error you mentioned.
However, it may work to start at the Case object and look up to the User, similar to the Lookup Relationships and Outer Joins section in the SOQL documentation. I'm not sure if this would work for you, but it's something you may want to try.
Here's an example:
-- Edit --
SELECT OwnerId, Owner.CustomField__c
FROM Case WHERE
CaseNumber LIKE '%1026'
ORDER BY CreatedDate DESC
LIMIT 1
Turns out Custom Fields are not accessible because OwnerId is a polymorphic key referencing either Group or User. That means the above won't work, sorry.
To work-around this is very complicated. You would have to create a custom lookup field called "User Owner", or something. That would store a lookup reference to the User, if the Owner is a User (this can be checked by comparing the beginning of OwnerId to '005', the User ID prefix). That field would need to be populated using a after insert, after update Trigger. All values for this new field would need to be dataloaded for previously existing Cases. But, once you have this new "User Owner" field, you can access custom fields on User through SOQL, using it.
ORDER BY and LIMIT don't make sense in your subquery because you're not returning records from the subquery. Instead, the subquery just builds a list of IDs used to filter the main query.
If you use a subquery in a way that the subquery records are returned, these clauses are fine. For example, this works:
SELECT Name,
(SELECT FirstName, LastName FROM Contacts ORDER BY LastName LIMIT 10)
FROM Account
I think it's worth adding that with new features of SOQL that were not available when this question was first asked and answered, that the approach of querying from Case should now be viable (with access to custom fields).
In particular the TYPEOF feature provides access to type specific fields through polymorphic lookups: http://www.salesforce.com/us/developer/docs/soql_sosl/Content/sforce_api_calls_soql_select_typeof.htm
It is worth noting that this feature is still in Developer Preview.
SELECT
TYPEOF Owner
WHEN User THEN CustomField__c
END
FROM CASE
Did you try switching your query around to be something like this?
SELECT OwnerId, (select id from user)
FROM Case
WHERE Case.CaseNumber LIKE '%1026'
ORDER BY Case.CreatedDate DESC LIMIT 1

Resources