Guías · Sector 4

SQL Server: copias de seguridad del ERP, restauración a un punto en el tiempo y unas filas borradas

El modelo de recuperación que necesita la base de datos del ERP, la pauta de copias completas, diferenciales y de log, la copia a un recurso compartido o a una URL con checksum, la retención, la cadena de restauración con NORECOVERY y STOPAT en un servidor de pruebas, el historial de msdb y cómo recuperar unas filas borradas a las 10:40 en una planta de otro país.

Qué vas a conseguir

Que la base de datos del ERP se pueda devolver a cualquier minuto de los últimos días, y que lo hayas comprobado antes de necesitarlo. En una empresa industrial con plantas en varios países el ERP no descansa: cuando en una sede es de noche, en otra se están cerrando pedidos, lanzando órdenes de fabricación o haciendo el cierre de mes. No hay ventana tranquila para copias largas ni margen para descubrir el día del problema que falta un log.

Antes de empezar

  • El nombre de la base de datos del ERP y de sus bases auxiliares (en los ejemplos, ERP_PROD en srv-sql-erp01).
  • Un servidor de pruebas (srv-sql-test01) con espacio para restaurar la base entera; nunca se restaura encima de producción para investigar.
  • El RPO y el RTO acordados con negocio: cuánto dato se puede perder y cuánto puede tardar la vuelta.
  • El contacto del consultor o partner del ERP: cualquier corrección de datos directa en tablas del ERP se hace con su visto bueno.
  • La zona horaria del servidor SQL y la de cada planta. Las horas de msdb y de STOPAT son las del servidor.

1. El modelo de recuperación

ModeloQué permiteCuándo
SIMPLESolo volver a la última completa o diferencialBases de pruebas, staging, informes que se regeneran
FULLCopias de log y restauración a un punto en el tiempoEl ERP de producción, siempre
BULK_LOGGEDComo FULL, pero las cargas masivas van mínimamente registradasSolo durante una carga puntual; no permite STOPAT dentro de ese log
SSMS · srv-sql-erp01 · comprobar el modelo
SELECT name, recovery_model_desc, log_reuse_wait_desc FROM sys.databases WHERE name LIKE 'ERP%';name          recovery_model_desc  log_reuse_wait_desc------------  -------------------  -------------------ERP_PROD      FULL                 LOG_BACKUPERP_PROD_DOC  SIMPLE               NOTHINGALTER DATABASE [ERP_PROD_DOC] SET RECOVERY FULL;

LOG_BACKUP es normal en una base FULL: el log espera a la siguiente copia de log. Si una base pasa a FULL, la cadena empieza con la siguiente copia completa; hasta entonces, las copias de log no sirven.

2. La pauta de copias y dónde se guardan

  • Completa semanal, la noche del sábado de la sede del servidor, revisando antes que no coincide con el cierre de mes de ninguna planta.
  • Diferencial diaria, para no tener que aplicar una semana de logs.
  • Log cada 15 minutos (o menos si el RPO lo pide), 24x7.
  • Siempre con CHECKSUM y COMPRESSION, y a un destino fuera del propio servidor: un recurso compartido de copias o una URL de almacenamiento de objetos.
SSMS · pauta de copias de ERP_PROD
# Completa, a un recurso compartido (la cuenta del servicio SQL necesita escritura)BACKUP DATABASE [ERP_PROD] TO DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_FULL_20261024.bak' WITH CHECKSUM, COMPRESSION, STATS = 10;# Diferencial diariaBACKUP DATABASE [ERP_PROD] TO DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_DIFF_20261026.bak' WITH DIFFERENTIAL, CHECKSUM, COMPRESSION;# Log cada 15 minutosBACKUP LOG [ERP_PROD] TO DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_LOG_20261027_1045.trn' WITH CHECKSUM, COMPRESSION;# Alternativa: a una URL con una credencial SAS creada para ese contenedorBACKUP DATABASE [ERP_PROD] TO URL = N'https://contosobkp.blob.core.windows.net/sql/ERP_PROD_FULL_20261024.bak' WITH CHECKSUM, COMPRESSION;# Comprobar que el fichero se puede leer y que los checksums cuadranRESTORE VERIFYONLY FROM DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_FULL_20261024.bak' WITH CHECKSUM;The backup set on file 1 is valid.
  • Un fichero por copia con fecha y hora en el nombre. Escribir siempre sobre el mismo .bak con INIT deja una única copia y ningún historial.
  • Si otra herramienta (el agente de la plataforma de copias) hace también copias del ERP, una de las dos debe usar COPY_ONLY; dos cadenas de diferenciales sobre la misma base terminan en una restauración que no encuentra su completa.
  • VERIFYONLY comprueba que la copia es legible, no que la base esté sana. Eso solo lo dice una restauración real y un DBCC CHECKDB sobre ella.

3. Retención

TipoEn el recurso compartidoCopia fuera de la sede
Completa5 semanasUna por mes, 12 meses (o lo que pida auditoría)
Diferencial14 díasNo
Log14 díasMientras exista la completa de la que dependen

La limpieza la hace un trabajo del SQL Server Agent o la plataforma de copias, nunca alguien borrando ficheros a mano: un log borrado rompe la cadena desde ese punto. Las copias de cierre de ejercicio se etiquetan y se guardan aparte, porque finanzas o auditoría las pedirán años después.

4. La cadena de restauración en el servidor de pruebas

Para volver a las 10:39 del martes, la cadena es: la última completa anterior, la última diferencial posterior a esa completa y anterior a las 10:39, y todos los logs desde esa diferencial hasta el que contiene las 10:39.

SSMS · srv-sql-test01 · restaurar ERP_PROD a las 10:39 con otro nombre
RESTORE DATABASE [ERP_PROD_1039] FROM DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_FULL_20261024.bak' WITH MOVE N'ERP_PROD' TO N'E:\SQLData\ERP_PROD_1039.mdf', MOVE N'ERP_PROD_log' TO N'F:\SQLLog\ERP_PROD_1039.ldf', NORECOVERY, STATS = 10;RESTORE DATABASE [ERP_PROD_1039] FROM DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_DIFF_20261026.bak' WITH NORECOVERY;RESTORE LOG [ERP_PROD_1039] FROM DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_LOG_20261027_0015.trn' WITH NORECOVERY;# ... un RESTORE LOG por cada fichero, en orden ...RESTORE LOG [ERP_PROD_1039] FROM DISK = N'\\srv-bkp01\sql$\ERP_PROD\ERP_PROD_LOG_20261027_1045.trn' WITH STOPAT = N'2026-10-27T10:39:00', RECOVERY;RESTORE LOG successfully processed 1874 pages in 0.412 seconds (35.532 MB/sec).
  • Los nombres lógicos para MOVE salen de RESTORE FILELISTONLY FROM DISK = ....
  • Mientras la base está en NORECOVERY se pueden seguir aplicando copias; RECOVERY la abre y cierra la cadena. Si no sabes el minuto exacto, restaura los logs con STANDBY = N'F:\SQLLog\ERP_PROD_1039.tuf' en lugar de NORECOVERY: la base queda en solo lectura entre log y log y puedes consultarla para ir acercándote.
  • Anota cuánto ha tardado, con DBCC CHECKDB al final: esa es tu cifra real de RTO para el ERP. Repite la prueba cada trimestre.

5. El historial de msdb

SSMS · srv-sql-erp01 · qué copias hay y dónde están
SELECT TOP (8) bs.type, bs.backup_start_date, bs.backup_finish_date, bs.has_backup_checksums, bs.is_copy_only, bmf.physical_device_name FROM msdb.dbo.backupset bs JOIN msdb.dbo.backupmediafamily bmf ON bs.media_set_id = bmf.media_set_id WHERE bs.database_name = N'ERP_PROD' ORDER BY bs.backup_finish_date DESC;type  backup_start_date    backup_finish_date   has_backup_checksums  is_copy_only  physical_device_name----  -------------------  -------------------  --------------------  ------------  ----------------------------------------------L     2026-10-27 10:45:00  2026-10-27 10:45:04  1                     0             \\srv-bkp01\sql$\ERP_PROD\ERP_PROD_LOG_20261027_1045.trnI     2026-10-26 23:00:00  2026-10-26 23:06:41  1                     0             \\srv-bkp01\sql$\ERP_PROD\ERP_PROD_DIFF_20261026.bakD     2026-10-24 22:00:00  2026-10-24 23:12:18  1                     0             \\srv-bkp01\sql$\ERP_PROD\ERP_PROD_FULL_20261024.bak

D es completa, I diferencial y L log. Con esta consulta montas la lista de ficheros de la cadena sin buscar a ojo en el recurso compartido. msdb.dbo.restorehistory, en el servidor de pruebas, deja constancia de cada restauración que haces para la prueba trimestral.

6. El ticket: "alguien ha borrado filas en el ERP a las 10:40, ¿se pueden recuperar?"

Martes. Desde una planta de otro país avisan de que han desaparecido líneas de pedidos de compra de un proveedor. El ERP sigue en uso en todas las sedes.

  1. Qué hora es "las 10:40": la de la planta, no la del servidor. Si la planta va una hora por detrás, para el servidor son las 11:40. Confírmalo con el usuario y con el log de auditoría del ERP si lo tiene.
  2. No se restaura producción: volver todo el ERP atrás borraría el trabajo del resto de plantas desde entonces. Se restaura una copia aparte y se recuperan solo esas filas.
  3. Asegura el log: lanza una copia de log ya (sin NORECOVERY, producción sigue abierta) para que el periodo del borrado esté en una copia y no dependa de la siguiente programada.
  4. Restaura en srv-sql-test01 la cadena hasta un minuto antes del borrado (sección 4), con otro nombre de base.
  5. Compara: las filas que están en la copia y no en producción, por la clave de la tabla, en una consulta con el consultor del ERP delante.
  6. Devuélvelas como diga el ERP: con su herramienta de importación, con un script revisado por el partner (cuidado con columnas IDENTITY, triggers y tablas relacionadas) o volviéndolas a introducir si son pocas. Lo que no se hace es un INSERT directo sin que nadie del ERP lo valide.
  7. Cierra con causa: quién borró, desde qué usuario y por qué pudo hacerlo. Muchas veces es un permiso de más en un perfil del ERP, no un fallo técnico.

Siguientes pasos

  • La guía de PostgreSQL/MySQL/SQL Server del Nivel 3 cubre la administración diaria de las bases de datos.
  • La de Veeam Backup explica cómo encaja la copia del servidor SQL con la de las máquinas virtuales.
  • La de Backup y DR/BCP del Nivel 4 sitúa el ERP dentro del plan de continuidad.

¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.

Tech Career Lab · newsletter

¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.

Apúntate y llévate las 33 ideas →