Categoría: Bases de datos

  • Configurar Database Mail en SQL Server: perfiles, cuentas y sp_send_dbmail

    Database Mail es el mecanismo nativo de SQL Server para enviar correos electrónicos directamente desde la base de datos — usado típicamente para alertas de Agent Jobs, notificaciones de errores, o reportes automáticos. Esta guía cubre cómo crear un perfil nuevo replicando la configuración de uno existente, junto con la prueba de envío y el rollback completo si algo sale mal.

    Add Post

    Conceptos: perfil, cuenta y su relación

    Database Mail separa la configuración en tres piezas independientes, relacionadas entre sí:

    • Cuenta (account) — contiene los datos técnicos de conexión SMTP: servidor, puerto, credenciales, si usa SSL, y el correo/nombre que aparece como remitente.
    • Perfil (profile) — una capa de abstracción por encima de una o varias cuentas. Es el nombre que efectivamente se usa al enviar un correo (por ejemplo, desde un Job de SQL Server Agent) — nunca se referencia la cuenta directamente.
    • Relación perfil-cuenta — vincula ambos, con un sequence_number que define el orden de intento: si la cuenta principal falla, Database Mail prueba la siguiente cuenta asociada al mismo perfil, en orden.

    Esta separación permite, por ejemplo, tener un mismo perfil con dos cuentas SMTP distintas como respaldo, sin que las aplicaciones o jobs que usan el perfil necesiten saber nada sobre el failover.

    Script de creación completo

    USE msdb;
    GO
    
    -- 1. Crear la cuenta SMTP
    EXEC msdb.dbo.sysmail_add_account_sp
        @account_name              = 'cuenta_nuevo_entorno',
        @description               = 'Cuenta SMTP para el nuevo entorno',
        @email_address             = '[email protected]',
        @display_name              = 'Alertas Sistema',
        @replyto_address           = NULL,
        @mailserver_name           = 'smtp.tudominio.com',
        @port                      = 25,
        @enable_ssl                = 0,
        @username                  = NULL,
        @password                  = NULL,
        @use_default_credentials   = 0;
    GO
    
    -- 2. Crear el perfil
    EXEC msdb.dbo.sysmail_add_profile_sp
        @profile_name = 'PERFIL_NUEVO_ENTORNO',
        @description  = 'Perfil de correo para el nuevo entorno';
    GO
    
    -- 3. Vincular la cuenta al perfil
    EXEC msdb.dbo.sysmail_add_profileaccount_sp
        @profile_name    = 'PERFIL_NUEVO_ENTORNO',
        @account_name    = 'cuenta_nuevo_entorno',
        @sequence_number = 1;
    GO
    
    -- 4. Autorizar el perfil para uso público (todos los usuarios de msdb)
    EXEC msdb.dbo.sysmail_add_principalprofile_sp
        @profile_name   = 'PERFIL_NUEVO_ENTORNO',
        @principal_name = 'public',
        @is_default     = 0;
    GO

    Qué hace cada procedimiento almacenado

    ProcedimientoFunción
    sysmail_add_account_spCrea la cuenta SMTP con los parámetros de conexión
    sysmail_add_profile_spCrea el perfil (el nombre lógico que se usará al enviar correos)
    sysmail_add_profileaccount_spVincula una cuenta existente a un perfil existente
    sysmail_add_principalprofile_spOtorga permiso de uso del perfil a un principal (usuario o rol de base de datos); public lo hace disponible para todos

    Sobre @is_default: cuando se marca en 1, ese perfil se usa automáticamente si un envío de correo no especifica ningún @profile_name explícitamente. Es recomendable dejarlo en 0 salvo que se quiera intencionalmente ese comportamiento, para evitar que jobs mal configurados terminen enviando correos por el perfil equivocado.

    Validación de la configuración

    Antes de dar por buena la configuración, conviene confirmar que las tres piezas quedaron correctamente enlazadas consultando las vistas de catálogo correspondientes:

    -- Ver todos los perfiles configurados
    SELECT * FROM msdb.dbo.sysmail_profile;
    
    -- Ver todas las cuentas SMTP configuradas
    SELECT * FROM msdb.dbo.sysmail_account;
    
    -- Ver la relación entre perfiles y cuentas
    SELECT * FROM msdb.dbo.sysmail_profileaccount;
    
    -- Ver qué principals tienen acceso a cada perfil
    SELECT * FROM msdb.dbo.sysmail_principalprofile;

    Prueba de envío

    EXEC msdb.dbo.sp_send_dbmail
        @profile_name = 'PERFIL_NUEVO_ENTORNO',
        @recipients   = '[email protected]',
        @subject      = 'Prueba de perfil',
        @body         = 'El perfil está funcionando correctamente.';

    sp_send_dbmail encola el correo en la cola interna de Database Mail (tabla msdb.dbo.sysmail_mailitems) — el envío real no es instantáneo, lo procesa un proceso externo (DatabaseMail.exe) que SQL Server lanza bajo demanda. Para verificar si el correo realmente salió o quedó atascado:

    -- Ver el estado del último correo enviado
    SELECT TOP 10 * 
    FROM msdb.dbo.sysmail_allitems
    ORDER BY send_request_date DESC;
    
    -- Ver errores específicos de envío, si los hay
    SELECT * FROM msdb.dbo.sysmail_event_log
    ORDER BY log_date DESC;

    La columna sent_status en sysmail_allitems indica si el correo quedó como unsent, sent, o failed — este es el primer lugar a revisar si sp_send_dbmail se ejecutó sin error pero el correo nunca llegó.

    Rollback completo

    Si es necesario deshacer toda la configuración (por ejemplo, tras una prueba en un entorno que no corresponde), el orden de eliminación es el inverso al de creación — primero se deben quitar las relaciones antes que los objetos base:

    USE msdb;
    GO
    
    -- 1. Quitar el permiso del principal sobre el perfil
    EXEC msdb.dbo.sysmail_delete_principalprofile_sp
        @profile_name = 'PERFIL_NUEVO_ENTORNO',
        @principal_name = 'public';
    GO
    
    -- 2. Desvincular la cuenta del perfil
    EXEC msdb.dbo.sysmail_delete_profileaccount_sp
        @profile_name = 'PERFIL_NUEVO_ENTORNO',
        @account_name = 'cuenta_nuevo_entorno';
    GO
    
    -- 3. Eliminar el perfil
    EXEC msdb.dbo.sysmail_delete_profile_sp
        @profile_name = 'PERFIL_NUEVO_ENTORNO';
    GO
    
    -- 4. Eliminar la cuenta SMTP
    EXEC msdb.dbo.sysmail_delete_account_sp
        @account_name = 'cuenta_nuevo_entorno';
    GO

    Requisito adicional: habilitar Database Mail

    Si es la primera vez que se configura Database Mail en una instancia, el subsistema puede estar deshabilitado por defecto — hay que activarlo a nivel de configuración avanzada antes de que cualquiera de los procedimientos anteriores funcione:

    EXEC sp_configure 'show advanced options', 1;
    RECONFIGURE;
    GO
    
    EXEC sp_configure 'Database Mail XPs', 1;
    RECONFIGURE;
    GO

    Notas prácticas

    • El puerto 25 sin SSL (como en el ejemplo) es común en relays SMTP internos dentro de una red corporativa, pero no es apropiado para enviar directamente a través de internet — para eso se necesita normalmente el puerto 587 con @enable_ssl = 1 y credenciales de autenticación.
    • Si @use_default_credentials = 0 y no se especifica @username/@password, la cuenta SMTP asume que el servidor de correo permite retransmisión (relay) sin autenticación desde la IP del servidor SQL — esto debe estar explícitamente autorizado en el servidor de correo o relay correspondiente.
    • Antes de dar por completada la configuración en un entorno productivo, siempre correr la prueba de sp_send_dbmail y confirmar en sysmail_allitems que el estado sea sent, no solo confirmar que el EXEC no arrojó error.
  • nextval() en Vertica: diferencias con PostgreSQL y cómo auditar secuencias

    Versión probada: Vertica 11.x — sintaxis y vistas de catálogo vigentes también en 12.x, 23.x, 24.x y la línea actual 26.x, sin cambios.

    Un caso común al revisar un DDL heredado en Vertica: encontrar una columna definida con DEFAULT nextval('esquema.secuencia') y necesitar confirmar que esa secuencia realmente existe, antes de asumir que el DDL es válido.

    id int NOT NULL DEFAULT nextval('public.seq_ejemplo')

    nextval() en Vertica vs. PostgreSQL

    Vertica sí soporta DEFAULT nextval('esquema.secuencia') en la definición de una columna, igual que PostgreSQL — pero con una diferencia importante que suele generar confusión a quienes vienen de ese motor: Vertica no crea secuencias automáticamente. En PostgreSQL, una columna SERIAL crea su secuencia asociada de forma implícita; en Vertica, la secuencia debe existir previamente, creada de forma explícita con CREATE SEQUENCE, antes de poder referenciarla en cualquier DEFAULT nextval(...).

    Verificar si una secuencia existe

    La vista correcta del catálogo del sistema para esto es v_catalog.sequences:

    SELECT *
    FROM v_catalog.sequences
    WHERE sequence_schema = 'public'
      AND sequence_name = 'seq_ejemplo';

    Para listar todas las secuencias existentes en la base de datos, con su valor actual:

    SELECT sequence_schema, sequence_name, current_value
    FROM v_catalog.sequences;

    Si la consulta devuelve una fila con un current_value numérico, la secuencia existe y está activa — el DDL que la referencia es válido y funcionará correctamente al insertar nuevas filas.

    Ver dependencias entre tabla y secuencia

    Para confirmar específicamente qué tabla(s) están usando una secuencia determinada (útil antes de eliminar o modificar una secuencia, para no romper nada):

    SELECT *
    FROM v_catalog.sequence_dependencies
    WHERE sequence_name = 'seq_ejemplo';

    Columnas IDENTITY vs. secuencias nombradas

    Vale la pena distinguir dos mecanismos distintos que Vertica ofrece para autoincrementar valores:

    • Columnas IDENTITY — se definen directamente en el DDL de la tabla (id IDENTITY(1,1)) y Vertica crea automáticamente una secuencia interna asociada, visible también en v_catalog.sequences, pero gestionada implícitamente junto con la tabla.
    • Secuencias nombradas — creadas explícitamente con CREATE SEQUENCE, y luego referenciadas manualmente con nextval('esquema.nombre') en el DEFAULT de una o más columnas. Este es el caso descrito en este artículo.

    La diferencia práctica más importante: una secuencia nombrada puede ser compartida entre varias tablas o columnas distintas si así se diseña, mientras que una columna IDENTITY está atada exclusivamente a la tabla donde se definió.

    Comportamiento distribuido de las secuencias

    Al ser Vertica un motor distribuido (MPP), las secuencias no funcionan exactamente igual que en un motor de un solo nodo: cada nodo del clúster mantiene su propia caché local de valores de secuencia durante una sesión, y las solicita de forma independiente conforme las va necesitando. Esto garantiza que todos los valores generados sean únicos, pero como consecuencia, los valores de nextval() pueden aparecer no consecutivos si distintos nodos están insertando datos en paralelo — es un comportamiento esperado y normal en Vertica, no un error.

    Detalle adicional: simplificar expresiones de timestamp

    Un ajuste menor mencionado durante la revisión del DDL — la expresión con cast explícito:

    (statement_timestamp())::timestamp

    Puede simplificarse, ya que statement_timestamp() ya retorna un tipo timestamp nativo, haciendo el cast redundante:

    DEFAULT statement_timestamp()

    Resumen del flujo de verificación

    1. Confirmar que la secuencia existe con SELECT * FROM v_catalog.sequences WHERE sequence_name = '...'.
    2. Si no existe, crearla explícitamente con CREATE SEQUENCE antes de referenciarla en cualquier DEFAULT nextval(...).
    3. Revisar v_catalog.sequence_dependencies antes de modificar o eliminar una secuencia en uso, para identificar todas las tablas que dependen de ella.
    4. Recordar que los valores pueden no ser consecutivos en un clúster con varios nodos activos — no es un bug, es el comportamiento distribuido esperado.