function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
du-scodouglasdu-scodouglas 

Where statement issues

Hello,

 

I am trying to write the SOQL statement that will let me select only contacts that belong to the triggering account in a trigger, atm I have:

 

    Contact[] cntList = [ SELECT c.Phone
                          FROM Contact c
                          WHERE Contact.AccountId = Account.Id
                          FOR UPDATE];

 

but the SOQL seems unhappy with the Account.Id field... giving the error:

 

Error: Compile Error: unexpected token: 'Account.Id' at line 9 column 52

 

I am new to APEX and Salesforce, I was hoping for either a nice link to some resources I can learn from, or perhaps an explanation on the error of my ways

 

Thanks alot.

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

You'll actually need to use something more like the following:

 

Contact[] cntList = [SELECT c.Id,c.Phone
                     FROM Contact c
                     WHERE Contact.AccountId in :Trigger.newMap.keySet()
                     FOR UPDATE];

You need to use "in" instead of "=", because it will be a list of account ID values, not necessarily just a singular account, and you need to actually obtain the list of account ID values; you referenced only a field name, not a valid variable that contains ID values. Trigger.newMap.keySet() returns a list of record ID values for the current trigger, which I assume is an Account After Update trigger. The ":" in the query tells the database system to perform dynamic variable substitution, which is necessary to pass a value or list of values into a query.

 

I hope this helps you out!

 

Edit: Be sure to take a look at the main page (http://developer.force.com/) and check out the resources under Technical Library. They have everything you need there: examples, documentation, and so on.

All Answers

sfdcfoxsfdcfox

You'll actually need to use something more like the following:

 

Contact[] cntList = [SELECT c.Id,c.Phone
                     FROM Contact c
                     WHERE Contact.AccountId in :Trigger.newMap.keySet()
                     FOR UPDATE];

You need to use "in" instead of "=", because it will be a list of account ID values, not necessarily just a singular account, and you need to actually obtain the list of account ID values; you referenced only a field name, not a valid variable that contains ID values. Trigger.newMap.keySet() returns a list of record ID values for the current trigger, which I assume is an Account After Update trigger. The ":" in the query tells the database system to perform dynamic variable substitution, which is necessary to pass a value or list of values into a query.

 

I hope this helps you out!

 

Edit: Be sure to take a look at the main page (http://developer.force.com/) and check out the resources under Technical Library. They have everything you need there: examples, documentation, and so on.

This was selected as the best answer
du-scodouglasdu-scodouglas

Wow thanks alot sfdcfox, that's the perfect solution.

 

Just for my own sake, I like to understand very clearly how the solution worked...

 

the method Trigger.newMap.keySet() is a method that belongs to the Trigger.newMap (which itself is a special object that references the AFTER update object that called the trigger?) and returns a list of account ID values?

sfdcfoxsfdcfox

Here's the short answer: yes.

 

Here's the grisly details:

 

Trigger is a static class (a class that exists in a global context, without your code defining it). It tells your code information about the state of the trigger (before insert, after update, etc), as well as the records that are affected. There are four static variables for the records being processed: old, new, oldMap, and newMap. State information are boolean functions such as Trigger.isBefore() and Trigger.isDelete().

 

Trigger.old and Trigger.new are List<Sobject> (a regular array with the data type of the trigger's records); Old is the value before the user initiated the transaction, new is the value that the user submitted as the new value. Old won't exist in insert and undelete triggers, and New won't exist in delete triggers.

 

Trigger.oldMap and Trigger.newMap are Map<Id,Sobject>, which is a "dictionary" class. Much like a physical dictionary, it has a set of "words" that it knows are associated with certain values ("definitions"). In this case, the "word", called a "key", is the ID of the record being processed, and its "definition", called the "value", is the record passed to the trigger's code.

 

You could consider a Map to be an array that accepts strings instead of numeric indices (a map can accept Integer as a key type, but this statement was only for metaphorical purposes). To get the keys, use "keyset()", for the values, use "values()", to see if a key is already in the map, use "containsKey(keyValue)", to get the value of a key, use "get(keyValue)", and to set a new value in its place, use "put(keyValue,dataValue)". Note that "put" won't work on the Trigger maps, because they are special. I included this for completion.

 

The "keys" of the maps are actually a Set<ID>. A Set is like the mathematical concept of a set (the union of numbers that are unique). For example, { 1 5 4 } is a set, while { 1 4 4 } is not a set (4 is repeated). It is similar to an array, but it knows what values it contains, and silently ignores duplicates. Sets contain methods such as "contains", to find if a certain value is in the set. From the set { 1 5 4 }, contains(5) would be true, while contains(2) would be false.

 

In a "before" trigger, you can use "addError" on any value to stop the record from saving, or you can modify the value in "new" or "newMap" to cause a different value to be saved to the database. You should not try to use remove, removeAll, add, addAll, etc methods that are normally available to Lists and Maps, because it won't work. In an "after" trigger, you shouldn't attempt to modify any values at all in the four static variables.

 

As you may have gathered, you can use triggers to do "if previous value is different than new value" checks, and make specific changes to data based on that input, and even more complex logic such as checking values against a different object, etc. If you modify the values in "new", and later throw an error, the user won't see the changes to the values (you can't use this as a "suggested correction" mechanism).

 

So, to completely answer the question, "Trigger" is a global class with static members, newMap is a Map<Id,Sobject> that gives you dictionary-style access to records, with a "key" for the record being processed, and its data value being the actual record, and keySet() is a function on that Map that returns a Set<Id>, which happens to be all of the ID values for all records involved in the trigger.

 

Suggested Reading: Apex Code Developer's Guide, in particular Set, Map, and Trigger.

naveenkumarbvnaveenkumarbv

Hi,

 

This may seem to be a trivial issue, but I need some help to solve this.

 

I am trying to query salesforce Contact entity using REST api from a RoR application.

I am able to retrieve all the Contact entities information  successfully when I only use a simple select query like:

 

@uri.get({:q => 'select id, firstname, lastname from Contact'}, headers) do |callback|
      callback.on_ok do |response|
        self.replace(response.deserialise['records'].map do |contact|
          Contact.new(@salesforce_session,
            'FirstName' => contact["FirstName"], 'LastName' => contact["LastName"], 'Id' => contact["Id"]
          )
        end)
      end

 

 

But, I am getting a 400 Bad Request error when using the where clause with the above statement:

 

@uri.get({:q => 'select id, firstname, lastname from Contact where email = "naveen@gmail.com"'}, headers) do |callback|
      callback.on_ok do |response|
        puts "******************* Printing Response ********************"
        puts response.inspect
        self.replace(response.deserialise['records'].map do |contact|
          Contact.new(@salesforce_session,
            'FirstName' => contact["FirstName"], 'LastName' => contact["LastName"], 'Id' => contact["Id"]
          )
        end)
      end

 

LOG:

 

Loading details by email id........
<- (GET -3363550491 -217275911) https://ap1.salesforce.com:443/services/data/v20.0/query?q=select+id%2C+firstname%2C+lastname+from+C...
-> (GET -3363550491 -217275911) 400 Bad Request (230 bytes 2.15s)

 

 

 I understand that there is something wrong in the Request used to query salesforce. How do I ensure that the request format is correct while using a 'WHERE' clause.

 

Thank You,

Naveen Kumar B.V

naveenkumarbvnaveenkumarbv

Doubt:

    Or do we have any property on columns in salesforce like "queryable" which has to be set.. in order to use them as filters while querying