Consultas de relación (padre e hijo)
Subir al padre con un punto, bajar a los hijos con una subconsulta: una sola consulta trae la cuenta, su propietario y todos sus contactos.
✓ En esta sub-lección
- 01Leer campos del registro padre con la notación de punto (Account.Name, Owner.Email).
- 02Traer los registros hijos con una subconsulta y recorrerlos con un bucle anidado.
- 03Distinguir el campo __c del nombre de relación __r en objetos personalizados.
- 04Filtrar por los hijos con IN (SELECT ...), como el filtro cruzado de un informe.
La nutria: ¿Te acuerdas? · Repaso de la lección 2
¿En qué orden van estas cláusulas en una consulta?
Los datos de Salesforce no viven sueltos: un contacto pertenece a una cuenta, una oportunidad tiene propietario, una factura tiene líneas. Como Admin ya diseñaste esas relaciones con campos de búsqueda y maestro-detalle. Esta lección enseña a recorrerlas desde una consulta, en las dos direcciones.
c.LastName→ 'Torres'el propio contactoc.Account.Name→ 'Northwind Trading'un punto: su cuentac.Account.Industry→ 'Retail'otro campo de la misma cuentac.Account.Owner.Name→ 'Marco Vidal'dos puntos: el propietario de su cuenta
Cada punto sube un nivel por un campo de búsqueda, igual que en una fórmula entre objetos. Hacia arriba puedes encadenar hasta cinco.
Hacia arriba: del hijo al padre
La nutria: El paralelo de Admin
List<Contact> contacts = [
SELECT Id, LastName, Email,
Account.Name, Account.Industry,
Owner.Name
FROM Contact
WHERE Account.Industry = 'Retail'
];
for (Contact c : contacts) {
System.debug(c.LastName + ' trabaja en ' + c.Account.Name);
}__c o __r: el campo o el camino
En objetos estándar el nombre de la relación coincide con el del campo sin «Id»: AccountId guarda el Id, Account es el camino. En objetos personalizados la regla es la misma con otro sufijo: el campo de búsqueda Customer__c guarda el Id de la cuenta, y Customer__r es el camino para subir a ella.
| Quieres… | Estándar (Contact → Account) | Personalizado (Invoice__c → Account) |
|---|---|---|
| el Id del padre | AccountId | Customer__c |
| un campo del padre | Account.Name | Customer__r.Name |
Hacia abajo: del padre a los hijos
La nutria: El paralelo de Admin
List<Account> accounts = [
SELECT Id, Name,
(SELECT Id, LastName, Email FROM Contacts ORDER BY LastName)
FROM Account
WHERE Industry = 'Retail'
];
for (Account a : accounts) {
System.debug(a.Name + ' tiene ' + a.Contacts.size() + ' contactos');
for (Contact c : a.Contacts) {
System.debug(' · ' + c.LastName);
}
}- En el FROM de la no va el nombre del objeto (Contact) sino el nombre de la relación hija, en plural: Contacts, Opportunities, Cases.
- En objetos personalizados es el «Child Relationship Name» que pusiste al crear la búsqueda, con __r: (SELECT Id FROM Invoices__r).
- La subconsulta admite su propio WHERE, ORDER BY y LIMIT, como el filtro y el orden de una related list.
- Una cuenta sin contactos llega con a.Contacts vacía, no null: puedes recorrerla sin miedo.
Filtrar por los hijos: el filtro cruzado
En los informes existe el filtro cruzado: «Cuentas con Oportunidades» o «Cuentas sin Casos». En SOQL se escribe con IN o NOT IN y una consulta que devuelve Ids. Es distinto de la subconsulta del SELECT: aquella trae los hijos; esta solo los usa para decidir qué padres entran.
// Cuentas CON al menos una oportunidad ganada
[SELECT Id, Name FROM Account
WHERE Id IN (SELECT AccountId FROM Opportunity WHERE IsWon = true)]
// Cuentas SIN ningún caso
[SELECT Id, Name FROM Account
WHERE Id NOT IN (SELECT AccountId FROM Case)]La nutria: ¿Y por qué no un Flow? Porque Get Records no empareja padres e hijos
Las tres formas, una al lado de otra
Bajar a los hijos, subir al padre y filtrar por los hijos se escriben parecido y devuelven cosas MUY distintas. Cambia de pestaña en el diagrama y fíjate en dos columnas: cuántas filas vuelven y de qué objeto son. Es el error más común al empezar: pedir contactos y recibir cuentas, o al revés.
[SELECT Name, (SELECT LastName FROM Contacts) FROM Account]| Account.Name | Contacts |
|---|---|
| Acme Corp | Rivera, Soto |
| Northwind Trading | Torres |
| Globex | Méndez, Ruiz, Pardo |
| Initech | (lista vacía) |
| Umbrella Foods | Díaz |
5 filas — Subconsulta en el SELECT: cada cuenta llega con SU lista de contactos colgando, como la related list de la página del registro. Devuelve las 5 cuentas, tengan contactos o no — Initech viene con la lista vacía, que no es null.