A Contact belongs to an Account. When you query the Contact, the parent Account is one hop away, and SOQL will walk that hop for you if you ask for it. The previous lesson went the other way: from an Account down to its Contacts. This lesson goes from a child up to its parent, and it is the direction you need the moment you are looking at a list of Contacts and want to know which company each one works for.
On a Contact record, the Account is a lookup field. In a query you can name it directly with the Account relationship name, and add a dot to reach a field on the parent:
List<Contact> contacts = [ SELECT Id, LastName, Account.Name FROM Contact WHERE Email = :contactEmail LIMIT 1 ]; System.debug(contacts[0].Account.Name);
Two details are load-bearing. The relationship name on the child side is singular — Account, not Accounts. Writing SELECT Accounts.Name FROM Contact fails to compile with Didn't understand relationship 'Accounts' in field path, because a lookup holds exactly one parent. This is the mirror image of the parent-to-child lesson, where the name was plural (Contacts) because an Account holds many children.
The second detail is that you can pull more than one parent field in the same hop. SELECT Account.Name, Account.Type gives you both the name and the type of the parent Account from a single query against Contact.
After the query, contacts[0].Account is a single Account record, not a list. That follows from the lookup being singular. You read the parent fields straight off it: contacts[0].Account.Name, contacts[0].Account.Rating.
There is a subtlety worth knowing. A Contact does not have to belong to an Account. When the parent is missing, contacts[0].Account is null, and reading contacts[0].Account.Name on that record throws a System.NullPointerException. Every Contact in the seed data has an Account, so this does not come up in the exercise — but a method that walks up to a parent should either guard for it or document that it assumes the parent is present.
There is a second way to reach the parent, and it is worth knowing because it is the shape you fall back on when the parent field name is not what you expect. Query the child for its foreign key, then query the parent by that key:
List<Contact> contacts = [SELECT AccountId FROM Contact WHERE Email = :contactEmail LIMIT 1];
if (contacts.isEmpty()) {
return null;
}
List<Account> accounts = [SELECT Name, Rating FROM Account WHERE Id = :contacts[0].AccountId];
This is two queries where the first shape was one. Use the relationship field when you can; reach for the two-step only when you need to do something the relationship field cannot do.
The same hop works in a WHERE clause. You can select the Contacts whose parent Account has a given type or rating, without writing a single join:
List<Contact> hotProspects = [ SELECT Id, LastName, Account.Name, Account.Rating FROM Contact WHERE Account.Type = 'Prospect' ];
The filter runs against the parent, and the rows that come back are still Contact rows. This is the single most useful thing the child-to-parent hop buys you: one query that answers a question about the parent while returning the children.
The first mistake is the plural relationship name. Accounts.Name reads fine in English but does not compile, because a lookup holds one parent, not many.
The second is reaching for contacts[0] without checking that the query matched anything. A query that matches no Contact returns an empty list, and contacts[0] on an empty list throws List index out of bounds: 0. Check isEmpty() first.
The third is forgetting that a parent field in a query is a field path, not a column on the child. SELECT Name FROM Contact compiles and returns the Contact's own Name field — it never touches the Account. If you want the Account's name you must write Account.Name. The two look similar and mean completely different things.
A list of Contacts usually has to say which company each contact belongs to. Without the relationship hop that is a second query per contact, and a page of twenty Contacts becomes twenty queries. With it, one query returns every Contact with its parent Account already attached, and the code that renders the list never learns about the Account record at all.