SQL Server Agent è lo scheduler integrato. Ogni backup che gira alle 2 di notte, ogni job di manutenzione che ricostruisce gli indici la domenica sera, ogni report orario che parte verso il finance — è Agent, che ticchetta in silenzio.
Finché qualcosa non fallisce silenziosamente, l’email di alert non parte perché l’operatore non era configurato, e lo scopri mercoledì quando il finance chiede “ehi, dov’è il report di lunedì?”
Questa lezione è su come costruire job di Agent che ti dicono davvero quando si rompono. Più l’equivalente Azure SQL Database, perché Azure non ha Agent in senso proprio.
Cos’è SQL Agent
Un servizio Windows separato (SQLSERVERAGENT) che gira accanto a SQL Server. Il suo lavoro: eseguire job pianificati. Agent vive dentro msdb, ed è per questo che la lezione 3 diceva che msdb è “dove vive SQL Agent.”
I job hanno:
- Step — uno o più. Ognuno è T-SQL, uno script PowerShell, un comando OS, un pacchetto SSIS, o pochi altri tipi.
- Schedule — quando girare. Pattern stile cron.
- Alert — cosa fare in caso di fallimento (mandare email all’operatore, lanciare un altro job, ecc.).
- Owner — di solito
sa; eseguire sotto un account impersonato è possibile.
Creare un job semplice
Via T-SQL (l’UI fa la stessa cosa dietro le quinte):
USE msdb;
GO
EXEC sp_add_job
@job_name = N'Runehold - Nightly Full Backup',
@enabled = 1,
@description = N'Esegue un backup FULL compresso di Runehold su D:\Backups';
EXEC sp_add_jobstep
@job_name = N'Runehold - Nightly Full Backup',
@step_name = N'Backup',
@subsystem = N'TSQL',
@command = N'
BACKUP DATABASE Runehold
TO DISK = N''D:\Backups\Runehold_FULL.bak''
WITH FORMAT, INIT, COMPRESSION, CHECKSUM;
',
@database_name = N'master';
EXEC sp_add_schedule
@schedule_name = N'Nightly 02:00',
@freq_type = 4, -- giornaliero
@freq_interval = 1, -- ogni giorno
@active_start_time = 020000; -- 02:00:00
EXEC sp_attach_schedule
@job_name = N'Runehold - Nightly Full Backup',
@schedule_name = N'Nightly 02:00';
EXEC sp_add_jobserver
@job_name = N'Runehold - Nightly Full Backup',
@server_name = @@SERVERNAME;
Brutto. La GUI di SSMS è di solito meno dolorosa per i job una tantum. Script come quello sopra sono per fare deploy dei job in più ambienti.
Alert e operatori
Di default, un job fallito scrive una voce nell’Event Log di Windows e… basta. Nessuno lo sa. Per essere notificato:
Configura Database Mail
SQL Server ha bisogno di un profilo di posta:
EXEC msdb.dbo.sysmail_add_account_sp
@account_name = N'Runehold SMTP',
@description = N'Account SMTP per le notifiche ops',
@email_address = N'ops@runehold.example',
@display_name = N'Runehold Ops',
@mailserver_name = N'smtp.runehold.example',
@port = 587,
@enable_ssl = 1,
@username = N'ops@runehold.example',
@password = N'secret';
EXEC msdb.dbo.sysmail_add_profile_sp @profile_name = N'Default';
EXEC msdb.dbo.sysmail_add_profileaccount_sp
@profile_name = N'Default',
@account_name = N'Runehold SMTP',
@sequence_number = 1;
Definisci un operatore
EXEC msdb.dbo.sp_add_operator
@name = N'DBA Team',
@enabled = 1,
@email_address = N'dba@runehold.example';
Di’ al job di notificare in caso di fallimento
EXEC msdb.dbo.sp_update_job
@job_name = N'Runehold - Nightly Full Backup',
@notify_level_email = 2, -- 2 = in caso di fallimento
@notify_email_operator_name = N'DBA Team';
Valori di notify_level_email: 0 (mai), 1 (in caso di successo), 2 (in caso di fallimento), 3 (al completamento).
Imposta sempre questo a 2 per ogni job. “Il job è girato ma è fallito in silenzio” è il singolo disastro operativo evitabile più comune.
Job multi-step
Un job può avere più step. Ogni step può fare branch su successo o fallimento:
- In caso di successo → vai allo step successivo, esci con successo, o salta a uno step specifico.
- In caso di fallimento → esci con fallimento, esci con successo (!), o salta altrove.
Il branch “esci con successo in caso di fallimento” è utile quando uno step è opzionale. Per esempio: “aggiorna stats; se fallisce per un motivo (lock), salta; se fallisce per un altro, abortisci.”
In generale: tieni gli step atomici e lascia che i fallimenti si propaghino. Inghiottire i fallimenti silenziosamente è come avvengono i disastri silenziosi.
Leggere lo storico dei job
L’UI mostra le esecuzioni recenti per ogni job. Programmaticamente:
SELECT TOP (20)
j.name AS job_name,
s.step_id,
s.step_name,
CASE h.run_status WHEN 0 THEN 'Failed' WHEN 1 THEN 'Succeeded' WHEN 2 THEN 'Retry' WHEN 3 THEN 'Cancelled' WHEN 4 THEN 'In Progress' END AS status,
CONVERT(VARCHAR, msdb.dbo.agent_datetime(h.run_date, h.run_time), 120) AS run_time,
h.run_duration,
h.message
FROM msdb.dbo.sysjobhistory AS h
JOIN msdb.dbo.sysjobs AS j ON j.job_id = h.job_id
LEFT JOIN msdb.dbo.sysjobsteps AS s
ON s.job_id = h.job_id AND s.step_id = h.step_id
ORDER BY h.run_date DESC, h.run_time DESC;
Mostra ogni step recente di ogni job con il suo esito. Esegui questo su un server che non conosci per vedere subito cosa si è rotto.
La trappola dell’espansione dei token
Il testo del comando di uno step può usare token Agent come $(ESCAPE_SQUOTE(JOBNAME)). Da SQL 2005, SQL Server richiede “escape macro” attorno ai token per prevenire injection. Un comando scritto senza fallirà con “Unable to parse token” o simile. Se erediti vecchi job, occhio a questo quando li modifichi.
Job Agent che tutti dovrebbero avere
Set minimo per ogni SQL Server di produzione:
- Backup FULL notturno (o DIFF + FULL giornaliero).
- Backup LOG frequenti (ogni 15-30 min se in recovery FULL).
IndexOptimizesettimanale — script di Ola, coperto nella lezione 35.DatabaseIntegrityChecksettimanale — wrapper di Ola attorno aDBCC CHECKDB.- Monitoring / heartbeat — un job che fallisce se qualcosa nel server non è in salute, emettendo un alert.
- Pulizia dello storico dei job Agent — Agent conserva lo storico per sempre a meno che tu non lo pulisca. Dopo un anno o due su un server occupato,
msdbdiventa enorme.
Lo script di installazione di Ola Hallengren (lezione 35) crea il #3 e il #4 per te.
Agent su Azure SQL Database
Azure SQL Database non ha Agent. L’equivalente sono gli Elastic Jobs: una risorsa Azure separata che esegue T-SQL secondo una pianificazione contro database target.
- Crea una risorsa Elastic Job Agent in Azure.
- I database target possono essere singoli o un intero server / pool.
- I job sono definiti in T-SQL e configurabili come pianificazione.
- Monitoraggio via portale Azure o DMV.
Azure SQL Managed Instance ha SQL Agent, quindi se stai migrando da on-prem e ti serve Agent, MI è il percorso drop-in.
Per Runehold, che gira su Azure SQL MI, usiamo Agent come on-prem. Se fossimo su Azure SQL DB, ricostruiremmo i job di manutenzione in Elastic Jobs.
Monitorare Agent dall’esterno di SQL
Per le aziende serie, mandare gli esiti dei job Agent a un sistema di monitoring centrale (Datadog, Grafana, Azure Monitor, Prometheus) non è opzionale. Lo strumento DBA preferito è:
- Uno script dedicato che gira ogni N minuti, interroga
msdb.dbo.sysjobhistory, ed emette una metrica per job per esito. - PowerShell o un agent sidecar fa il polling.
Una volta che esiste, “un job di backup che non è girato” diventa un alert nei tuoi normali canali ops, non una sorpresa il mercoledì.
Esegui questo sulla tua macchina
USE msdb;
GO
-- 1. Vedi i job che esistono
SELECT name, enabled, date_created, date_modified FROM msdb.dbo.sysjobs;
-- 2. Vedi lo storico recente
SELECT TOP (10)
j.name AS job_name,
CASE h.run_status WHEN 0 THEN 'Failed' WHEN 1 THEN 'Succeeded' ELSE CAST(h.run_status AS VARCHAR) END AS status,
CONVERT(VARCHAR, msdb.dbo.agent_datetime(h.run_date, h.run_time), 120) AS run_time,
h.message
FROM msdb.dbo.sysjobhistory AS h
JOIN msdb.dbo.sysjobs AS j ON j.job_id = h.job_id
ORDER BY h.run_date DESC, h.run_time DESC;
-- 3. Crea un job semplice di smoke-test
EXEC sp_add_job @job_name = N'Smoke Test';
EXEC sp_add_jobstep @job_name = N'Smoke Test',
@step_name = N'Check',
@subsystem = N'TSQL',
@command = N'SELECT GETDATE() AS ping;',
@database_name = N'master';
EXEC sp_add_schedule @schedule_name = N'Every 5 min',
@freq_type = 4, @freq_interval = 1,
@freq_subday_type = 4, @freq_subday_interval = 5;
EXEC sp_attach_schedule @job_name = N'Smoke Test', @schedule_name = N'Every 5 min';
EXEC sp_add_jobserver @job_name = N'Smoke Test', @server_name = @@SERVERNAME;
-- 4. Eseguilo adesso
EXEC sp_start_job @job_name = N'Smoke Test';
-- 5. Controlla lo storico
SELECT TOP (5) * FROM msdb.dbo.sysjobhistory
WHERE job_id = (SELECT job_id FROM msdb.dbo.sysjobs WHERE name = N'Smoke Test')
ORDER BY run_date DESC, run_time DESC;
-- 6. Pulisci
EXEC sp_delete_job @job_name = N'Smoke Test';
Agent è poco glamour. Gira che qualcuno lo guardi o no. Questo lo rende il tuo migliore amico o il tuo nemico più silenzioso, a seconda che tu abbia cablato gli alert.
Prossimo: sicurezza — login, utenti, ruoli, e perché il ruolo public è la trappola in cui cade quasi tutti.