Apex Academy
Módulo 4·02·25 min

Database.insert y resultados parciales

Todo o nada frente a «guarda lo que puedas y dime qué falló»: la misma decisión que tomas cada vez que cargas un CSV con Data Loader.

En Admin esLos archivos success.csv y error.csv de Data Loader

✓ En esta sub-lección

  1. 01Explicar qué hace insert cuando falla un solo registro de la lista.
  2. 02Usar Database.insert(lista, false) y leer cada SaveResult.
  3. 03Decidir cuándo conviene todo o nada y cuándo resultados parciales.

La nutria: ¿Te acuerdas? · Repaso de la lección 1

Insertas una cuenta con insert acc;. ¿Cuándo tiene Id la variable acc?

Insertas 200 leads y uno tiene un correo mal formado, que el campo Email rechaza por sí solo. ¿Qué debería pasar con los otros 199? Depende del negocio, y Apex te deja elegir. La instrucción insert de la lección anterior elige por ti; los métodos de la clase Database te dejan decidir.

La nutria: El paralelo de Admin

Cuando yo cargaba 1.000 filas con Data Loader y 3 fallaban, al terminar tenía dos archivos: success.csv con las 997 guardadas (y sus Ids nuevos) y error.csv con las 3 rechazadas y el motivo de cada una. Data Loader guarda lo que puede y te cuenta el resto. Eso es exactamente Database.insert(lista, false).

LA CARGA DE ESTA NOCHE

  • Ruiz · Acme
  • Kim · Globex
  • Silva · Initech

Paso 1 de 4

Tres leads del formulario web. Kim trae el correo mal escrito. Pulsa Reproducir.

Misma lista, mismo registro malo, dos resultados distintos.

insert: todo o nada

Con la instrucción insert (y update, delete, upsert), si un solo registro de la lista falla, no se guarda ninguno y se lanza una DmlException que detiene tu código. Es lo que quieres cuando los registros solo tienen sentido juntos: un pedido y sus líneas, una cuenta y su contacto principal.

Database.insert(lista, false): lo que se pueda

Apex
List<Lead> leads = new List<Lead>{
    new Lead(LastName = 'Ruiz', Company = 'Acme', Email = 'ruiz@acme.com'),
    new Lead(LastName = 'Kim',  Company = 'Globex', Email = 'no-es-un-email'),
    new Lead(LastName = 'Silva', Company = 'Initech', Email = 'silva@initech.com')
};

List<Database.SaveResult> results = Database.insert(leads, false);

for (Integer i = 0; i < results.size(); i++) {
    Database.SaveResult sr = results[i];
    if (sr.isSuccess()) {
        System.debug('Guardado: ' + sr.getId());
    } else {
        for (Database.Error err : sr.getErrors()) {
            System.debug(leads[i].LastName + ' falló: ' + err.getMessage());
        }
    }
}
Ruiz y Silva se guardan; Kim no. El código no se detiene: tú decides qué hacer con el error.
  • El segundo parámetro se llama allOrNone. true (o no ponerlo) se comporta como insert; false permite resultados parciales.
  • Devuelve una lista de Database.SaveResult en el mismo orden que la lista de entrada. results[1] es el resultado de leads[1]: por eso el for con índice, del Módulo 2, y no el for-each.
  • isSuccess() dice si se guardó; getId(), el Id nuevo; getErrors(), una lista de Database.Error con getMessage(), getStatusCode() y getFields(). Es tu error.csv, fila a fila.
  • Existen igual Database.update, Database.upsert (con UpsertResult e isCreated()) y Database.delete (con DeleteResult).

¿Cuál uso?

SituaciónElige
Una cuenta y su contacto principal: sin uno, el otro sobrainsert (todo o nada)
Carga nocturna de leads de un formulario webDatabase.insert(lista, false) y registrar los fallos
Actualizar la valoración de 500 cuentas independientesDatabase.update(lista, false)
Una factura y sus líneasTodo o nada (y en la lección 5, savepoints)

¿Todo o nada, o lo que se pueda?

La elección no es técnica, es de negocio, y se decide con una pregunta: ¿los registros de la lista dependen unos de otros? Si son independientes (leads de un formulario, casos de un correo), guardar los buenos y apuntar los malos es lo sensato: un lead sin empresa no tiene por qué impedir que entren los otros 199. Si forman una unidad (una cuenta con sus contactos, un pedido con sus líneas), una parte guardada sin la otra es un dato roto, y entonces quieres todo o nada. Esa segunda situación es la de la lección 5.

La nutria: El motivo del error lo escribiste tú

Esto me encanta: cuando un registro falla por una regla de validación, getMessage() devuelve exactamente el mensaje de error que escribiste al crear la regla, el mismo que ve un usuario en pantalla y el mismo que aparece en la columna ERROR del error.csv de Data Loader. Por eso vale la pena escribir mensajes de validación que expliquen qué corregir: desde hoy también los leerá tu código.

La nutria: ¿Y por qué no un Flow? Porque Marketing quiere la lista

Desde Winter '25, Create Records puede guardar parcialmente: los leads buenos entran aunque haya uno malo. Pero el flow no recibe qué registros fallaron ni por qué, y Marketing quiere exactamente esa lista para corregirlos por la mañana. Con Database.insert(leads, false), cada SaveResult te dice cuál falló y su motivo, como el error.csv de Data Loader.