Buscar texto en todas las tablas de una base de datos SQL Server

Ejecutar lo siguiente

 DECLARE @StrValorBusqueda nvarchar(100)
 
 SET @StrValorBusqueda='texto a buscar'
 
 DECLARE @Resultado TABLE (NombreColumna nvarchar(370), ValorColumna nvarchar(3630))
 
 SET NOCOUNT ON DECLARE @NombreTabla nvarchar(256), 
 
 @NombreColumna nvarchar(128), 
 @StrValorBusqueda2 nvarchar(110)
 
 SET @NombreTabla = ''
 SET @StrValorBusqueda2 = QUOTENAME('%' + @StrValorBusqueda + '%','''')
 WHILE @NombreTabla IS NOT NULL
 
 BEGIN
 SET @NombreColumna = ''
 SET @NombreTabla = 
 
 (SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
 FROM INFORMATION_SCHEMA.TABLES
 WHERE TABLE_TYPE = 'BASE TABLE'
 AND QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @NombreTabla
 AND OBJECTPROPERTY(
 OBJECT_ID(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)), 'IsMSShipped') = 0)
 WHILE (@NombreTabla IS NOT NULL) AND (@NombreColumna IS NOT NULL)
 BEGIN
 SET @NombreColumna =
 (SELECT MIN(QUOTENAME(COLUMN_NAME))
 FROM INFORMATION_SCHEMA.COLUMNS
 WHERE TABLE_SCHEMA = PARSENAME(@NombreTabla, 2)
 AND TABLE_NAME = PARSENAME(@NombreTabla, 1)
 AND DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar')
 AND QUOTENAME(COLUMN_NAME) > @NombreColumna)
 IF @NombreColumna IS NOT NULL
 BEGIN
 INSERT INTO @Resultado
 EXEC
 ('SELECT ''' + @NombreTabla + '.' + @NombreColumna + ''', LEFT(' + @NombreColumna + ', 3630) 
 FROM ' + @NombreTabla + ' (NOLOCK) ' + ' WHERE ' + @NombreColumna + ' LIKE ' + @StrValorBusqueda2)
 END 
 END 
 END
 SELECT NombreColumna, ValorColumna FROM @Resultado

 

usar tablas temporales en sql dinamico

Caso: Se tiene una consulta SQL que se genera dinamicamente, se quiere volcar el conjunto de resultados en una tabla temporal. Se tuvo que realizar lo siguiente:


ALTER PROCEDURE [dbo].[SPS_GeneraRptPedidoDetalle]    
    @docFechaInicio [date],
    @docFechaFin [date],
    @codCliente [int],
    @sucursal [char](4),
    @ordenCompra [char](15),
    @fAprobInicio [date],
    @fAprobFin [date],
    @vendedor [int],
    @item [char](15),
    @nroDoc [char](14),
    @linea [char](6),
    @familia [char](6),
    @subfamilia [char](6)
AS
BEGIN

    DECLARE @cadenaTotal [nvarchar](max)
    DECLARE @cadenaSql [varchar](MAX)
    DECLARE @cadenaWhere [nvarchar](MAX)
    DECLARE @cadenaOrderBy [varchar](200)
    DECLARE @cadenaGroupBy [varchar](MAX)
    
    SET @cadenaTotal=N''
    SET @cadenaWhere=N''                 
    
    SET @cadenaSql='SELECT   
             t2.ClienteReferencia,
             t2.ClienteNumero,
             t2.ClienteNombre,
             t1.NumeroDocumento,
             t1.TipoDetalle,
             t1.Linea as LineaDoc,
             [t10].[DescripcionLocal] as Marca,
             t1.ItemCodigo,
             t1.Descripcion,
             t1.UnidadCodigo,
             t1.CantidadPedida,
             t1.Monto,
             t1.DocumentoRelacNumeroDocumento,
             t1.DocumentoRelacLinea,
             t1.ExportacionMarcaPaquete,             
             t2.MonedaDocumento,
             [t9].[DescripcionCorta] AS GlosaMoneda,
             t1.Estado,
             t2.Vendedor,
             [t8].[NombreCompleto] AS AsesorComercial,
             t1.IGVExoneradoFlag,
             t2.Criteria,
             t2.FechaDocumento,
             t2.FechaAprobacion,
             t1.UnidadCodigoDoble,
             t1.CantidadPedidaDoble,
             t2.Comentarios,
             t1.DocumentoRelacTipoDocumento,
             t1.TipoDocumento,
             t1.Lote,
             t1.CantidadEntregada,
             t1.PrecioUnitario,
             t1.AlmacenCodigo,
             t1.Condicion,
             t1.PorcentajeDescuento01,
             t1.PorcentajeDescuento02,
             t1.PorcentajeDescuento03,
             t1.TransferenciaGratuitaFlag,
             t1.PrecioUnitarioGratuito,
             t1.PrecioUnitarioOriginal,
             t1.MontoFinal,
             t1.PrecioUnitarioFinal,
             t2.NumeroInterno,             
             t1.NumeroSerie,
             t2.CentroCosto,
             t3.Linea as LineaItem,
             t3.Familia,
             t3.SubFamilia,
             t3.NumeroDeParte,
             t3.ItemProcedencia,
             t1.ExportacionFechaProgramacion,
             t1.exportacioncomentarios,
             t2.ClienteRUC,
             t2.RequisicionNumero,
             t4.StockActual AS StockActual,
             t4.StockActual - ISNULL(t4.StockComprometido, 0) AS StockDisponible,
             t4.StockComprometido AS StockComprometido,
             t5.StockProceso,
             t6.FechaPrometida,
             t7.ClasificacionRotacion,
             [t2].[ClienteReferencia] AS NroOC,
             (sum(t1.[CantidadPedida] - [t1].[CantidadEntregada])*t1.[PrecioUnitario]) AS Valorizado,
             (sum(t1.[CantidadPedida] - [t1].[CantidadEntregada])*t1.[PrecioUnitario]*(CASE t2.[MonedaDocumento] WHEN ' + char(39) +  'LO' + char(39) + ' THEN (1/[t2].[TipodeCambio]) ELSE 1 END )) AS ValorizadoCambio        
    INTO #tmpTemporal             
    FROM     spring.[dbo].CO_DocumentoDetalle t1
             INNER JOIN spring.[dbo].CO_Documento t2 ON (t2.CompaniaSocio = t1.CompaniaSocio)
                AND (t2.TipoDocumento = t1.TipoDocumento)
                AND (t2.NumeroDocumento = t1.NumeroDocumento)
             LEFT OUTER JOIN spring.[dbo].WH_ItemMast t3 ON t1.ItemCodigo = t3.Item
             LEFT OUTER JOIN spring.[dbo].VW_WH_ItemAlmacenStock t4 ON t1.ItemCodigo = t4.Item
                AND t1.AlmacenCodigo = t4.AlmacenCodigo
             LEFT OUTER JOIN spring.[dbo].VW_WH_ItemComprasEnProceso t5 ON t5.Item = t1.ItemCodigo
                AND t5.Companiasocio = t1.Companiasocio
             LEFT OUTER JOIN spring.[dbo].VW_WH_ItemComprasEnProcesoFecha t6 ON t6.Item = t1.ItemCodigo
                AND t6.Companiasocio = t1.Companiasocio
             LEFT OUTER JOIN spring.[dbo].WH_ItemAlmacen t7 ON t7.AlmacenCodigo = t2.AlmacenCodigo
                AND t7.Item = t1.ItemCodigo
                AND t7.Condicion = ' + char(39) + '0' + char(39) + '
             LEFT OUTER JOIN [Spring].[dbo].[PersonaMast] t8 ON t8.[Persona]=[t2].[Vendedor]
             LEFT OUTER JOIN [spring].[dbo].[MonedaMast] t9 ON t9.[MonedaCodigo]=[t2].[MonedaDocumento]
             LEFT OUTER JOIN [Spring].[dbo].[WH_Marcas] t10 ON t10.[MarcaCodigo]=[t3].[MarcaCodigo]
    WHERE    (t1.TipoDocumento = ' + char(39) + 'PE' + char(39) + ')
             AND (t1.Estado = ' + char(39) + 'PR' + char(39) + ')
             AND t1.CantidadPedida > t1.CantidadEntregada
             AND t2.Estado = ' + char(39) + 'AP' + char(39) + '
             AND (t2.CompaniaSocio = ' + char(39) + '01000000' +  char(39) + ')                          
             AND (t2.FormaFacturacion <> ' + char(39) + 'GF' + char(39) + ')
             AND (t2.TipoVenta <> ' + char(39) + 'SEP' + char(39) + ')' + 
             ' AND (CAST(t2.FechaDocumento AS date) BETWEEN @docFechaInicio AND @docFechaFin)'

    
                 
    IF @codCliente<> 0 BEGIN
        SET @cadenaWhere= @cadenaWhere + N' AND [t2].[ClienteNumero]=@codCliente'    
    END 
    
    IF @sucursal<>'0' BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t2].[Sucursal]=@sucursal'
    END 
    
    IF @ordenCompra<>'0' BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t2].[ClienteReferencia]=@ordenCompra'
    END 
    
    IF YEAR(@fAprobInicio)<>1900 AND YEAR(@fAprobFin)<>1900 BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND CAST(ISNULL([t2].[FechaAprobacion],' + char(39) + '19000101' + char(39) + ') AS date) BETWEEN @fAprobInicio AND @fAprobFin'
    END 

    IF @vendedor<>0 BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t2].[Vendedor]=@vendedor'
    END 
    
    IF @item<>'0' BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t1].[ItemCodigo]=@item'
    END 
    
    IF @nroDoc<>'0' BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t1].[NumeroDocumento]=@nroDoc'
    END 
    
    IF @linea<>'0' BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t3].[Linea]@linea'
    END 
    
    IF @familia<>'0' BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t3].[Familia]=@familia'
    END 
    
    IF @subfamilia<>'0' BEGIN
        SET @cadenaWhere=@cadenaWhere + N' AND [t3].[SubFamilia]=@subfamilia'
    END     
    
    SET @cadenaGroupBy=N' GROUP BY   
             t1.NumeroDocumento,
             t1.TipoDetalle,
             t1.Linea,
             [t10].[DescripcionLocal],
             t1.ItemCodigo,
             t1.Descripcion,
             t1.UnidadCodigo,
             t1.CantidadPedida,
             t1.Monto,
             t1.DocumentoRelacNumeroDocumento,
             t1.DocumentoRelacLinea,
             t1.ExportacionMarcaPaquete,
             t2.ClienteNumero,
             t2.ClienteNombre,
             t2.MonedaDocumento,
             [t9].[DescripcionCorta],
             t1.Estado,
             t2.Vendedor,
             [t8].[NombreCompleto],
             t1.IGVExoneradoFlag,
             t2.Criteria,
             t2.FechaDocumento,
             t2.FechaAprobacion,
             t1.UnidadCodigoDoble,
             t1.CantidadPedidaDoble,
             t2.Comentarios,
             t1.DocumentoRelacTipoDocumento,
             t1.TipoDocumento,
             t1.Lote,
             t1.CantidadEntregada,
             t1.PrecioUnitario,
             t1.AlmacenCodigo,
             t1.Condicion,
             t1.PorcentajeDescuento01,
             t1.PorcentajeDescuento02,
             t1.PorcentajeDescuento03,
             t1.TransferenciaGratuitaFlag,
             t1.PrecioUnitarioGratuito,
             t1.PrecioUnitarioOriginal,
             t1.MontoFinal,
             t1.PrecioUnitarioFinal,
             t2.NumeroInterno,
             t2.ClienteReferencia,
             t1.NumeroSerie,
             t2.CentroCosto,
             t3.Linea,
             t3.Familia,
             t3.SubFamilia,
             t3.NumeroDeParte,
             t3.ItemProcedencia,
             t1.ExportacionFechaProgramacion,
             t1.exportacioncomentarios,
             t2.ClienteRUC,
             t2.RequisicionNumero,
             t4.StockActual,
             t4.StockComprometido,
             t5.StockProceso,
             t6.FechaPrometida,
             t7.ClasificacionRotacion,
             [t2].[ClienteReferencia],
             t2.[TipodeCambio]'
    
    SET @cadenaOrderBy=' ORDER BY t2.FechaDocumento ASC
           , [t2].[ClienteNombre] ASC          
           , t1.NumeroDocumento ASC
           , t1.Linea ASC
           , t2.MonedaDocumento ASC;'

    DECLARE @sqlTemporal [varchar](500)
    
    SET @sqlTemporal='SELECT *, (SELECT SUM(t2.Monto) FROM #tmpTemporal t2 WHERE ISNULL(t2.ClienteReferencia,' + char(39) + char(39) + ')=ISNULL(t1.ClienteReferencia,' + char(39) + char(39) + ') AND t2.ClienteNumero=t1.ClienteNumero) AS MontoTotal
    FROM #tmpTemporal t1;
    
    DROP TABLE #tmpTemporal;'


    SET @cadenaTotal=@cadenaSql+ @cadenaWhere + @cadenaGroupBy + @cadenaOrderBy + @sqlTemporal
    
        
    EXEC sp_executesql @cadenaTotal, N'@docFechaInicio [date], @docFechaFin [date], @codCliente [int], @sucursal [char](4)
    , @ordenCompra [char](15), @fAprobInicio [date], @fAprobFin [date], @vendedor [int], @item [char](15), @nroDoc [char](14)
    , @linea [char](6), @familia [char](6), @subfamilia [char](6)'
    , @docFechaInicio, @docFechaFin, @codCliente, @sucursal
    , @ordenCompra, @fAprobInicio, @fAprobFin, @vendedor, @item, @nroDoc
    , @linea, @familia, @subfamilia


    

END 

GO 
  

Configurar Correos SQL 2008 R2

Contents

Introduction

This article describes how to configure, test, and manage Database Mail in SQL Server 2008 R2. Database Mail was first introduced as a new feature in SQL Server 2005 and replaces the SQL Mail feature found in previous versions.

Database Mail is a SQL Server component that provides developers and administrators with an enterprise solution for sending e-mail messages from the SQL Server Database Engine. While the primary use of Database Mail is to send alert messages to administrators, it can also be used to send the results of a query to end users, use it in Transact-SQL code, or even include file attachments from any available resource on the network.

Database Mail versus SQL Mail

As previously mentioned, database Mail was first introduced as a new feature in SQL Server 2005 and replaces the SQL Mail feature found in previous versions. SQL Mail is still retained in SQL Server 2005 and SQL Server 2008 for backwards compatibility meaning that the SQL Server Agent can still use both SQL Mail and the new Database Mail feature (recommended) to send e-mail messages.

SQL Mail was designed to utilize an Extended Messaging Application Programming Interface (Extended MAPI) to send e-mail notifications. This required users to install and configure an e-mail application (such as Outlook) that supported the Extended MAPI communication protocol on the computer that is running SQL Server. Database Mail, on the other hand, has no such requirement as it utilizes the Simple Mail Transfer Protocol (SMTP) instead of Extended MAPI to send mail. Database Mail not only simplifies email setup, but has many other benefits over SQL Mail, including the following:

  • There is no requirement that an email client be installed on the SQL Server machine.
  • Email is queued for later delivery if the mail server stops or fails.
  • Multiple SMTP servers can be specified so that mail continues to be delivered in the event that one of the SMTP servers stops.
  • Database Mail is cluster aware.

Receiving Email

The Database Mail feature is used to send e-mail messages from the SQL Server Database Engine. The only way to receive e-mail messages in SQL Server 2008 is by using the legacy stored procedures like sp_processmail found in SQL Mail. Because there is no IMAP or POP3 support, Database Mail does not support receiving incoming e-mail messages. Receiving e-mail can present a security risk to the database server through the use of Denial-Of-Service (DOS) attacks or messages that contain harmful Transact-SQL likeDROP DATABASE.

Configure Database Mail – (Database Mail Configuration Wizard)

Before using Database Mail, several configuration tasks need to be carried out. This section describes the steps necessary to successfully configure Database Mail which includes verifying that the SQL Server Agent is running, verifying that the Service Broker is enabled for the msdb database, enabling the Database Mail feature, and finally, creating an e-mail Profile and SMTP mail account using the Database Mail Configuration Wizard.

Verify SQL Server Agent is Running

Messages in Database Mail are sent by the SQL Server Agent. If the Agent is not running, the messages will be accumulated in the msdb database and sent when the Agent service gets started again.

Use the master..xp_servicecontrol system stored procedure to check the status of the SQL Server Agent process. If the Agent is stopped, you will want to start it in order to send messages through Database Mail.

USE master
Go

EXEC xp_servicecontrol N'QueryState', N'SQLServerAGENT';
Go

Current Service State
---------------------
Running.

Verify Service Broker is Enabled for the msdb Database

Database Mail relies on Service Broker for queuing e-mail messages. If Database Mail is stopped or if Service Broker message delivery is not activated in the msdb database, Database Mail queues messages in the database but cannot deliver the messages. In this case, the Service Broker messages remain in the Service Broker Mail queue. Service Broker does not activate the external program, so there are no log entries in sysmail_event_log and no updates to the item status in sysmail_allitems and the related views.

Verify that Service Broker is enabled for the msdb database by running the following query.

USE master
Go

SELECT  is_broker_enabled
FROM    sys.databases
WHERE   database_id = DB_ID(N'msdb');
Go

is_broker_enabled
-----------------
1

is_broker_enabled will be 1 if Service Broker is enabled for the given database (msdb), otherwise it will be 0.

If the Service Broker is disabled (is_broker_enabled = 0), run the following T-SQL to enable Service Broker for the msdb database:

USE master
Go

ALTER DATABASE msdb SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;
Go

Enable Database Mail

Database Mail relies on a number of internal stored procedures. To reduce the surface area, these stored procedures are disabled on new installations of SQL Server which makes Database Mail not active by default. Users must explicitly enable these Database Mail stored procedures by using either one of the the following three methods.

  1. sp_configure Stored Procedure

    To enable Database Mail using the sp_configure stored procedure, run the following.

    USE master
    Go
    
    EXEC sp_configure 'show advanced options', 1
    Go
    
    Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
    
    RECONFIGURE
    Go
    
    Command(s) completed successfully.
    
    EXEC sp_configure 'Database Mail XPs', 1
    Go
    
    Configuration option 'Database Mail XPs' changed from 0 to 1. Run the RECONFIGURE statement to install.
    
    RECONFIGURE
    Go
    
    Command(s) completed successfully.
    
    EXEC sp_configure 'show advanced options', 0
    Go
    
    Configuration option 'show advanced options' changed from 1 to 0. Run the RECONFIGURE statement to install.
    
    RECONFIGURE
    Go
  2. Surface Area Configuration Facet of Policy-Based Management

    To enable Database Mail using the Surface Area Configuration Facet of Policy-Based Management, launch SQL Server Management Studio, right-click on the server that you intend to enable the Database Mail feature for and select Facets.

    Open Facets Dialog Box for Server

    On the Facets dialog box, select Surface Area Configuration from the Facet drop down list.

    Select the Surface Area Configuration Facet

    Locate DatabaseMailEnabled in the Facet properties section and change its value from False to True and then click the Ok button.

    Enable Database Mail Feature
  3. Database Mail Configuration Wizard

    The third method that can be used to enable the Database Mail feature is to do nothing here and wait until you run the Database Mail Configuration Wizard (next section). The Database Mail Configuration Wizard is used to create e-mail Profiles and SMTP Accounts; both of which are required in order to use Database Mail to send messages. If the Database Mail feature in not enabled, the Database Mail Configuration Wizard will prompt you to enable it when attempting to create the e-mail Profile and SMTP account.

    Enable Database Mail through the Database Mail Configuration Wizard

Create E-Mail Profile and SMTP Account

After enabling the Database Mail feature, the next step is to use the Database Mail Configuration Wizard to create an e-mail Profile and an SMTP Account through which to send emails. To start the Database Mail Configuration Wizard, in Object Explorer, expand theManagement folder in SQL Server Management Studio, right-click Database Mail, and select Configure Database Mail.

Launch Database Mail Configuration Wizard

This will bring up the Database Mail Configuration Wizard welcome screen. Click Next to continue.

    
Welcome to Database Mail Configuration Wizard

On the Select Configuration Task screen, select Set up Database Mail by performing the following tasks:. Click Next to continue.

    
Select Configuration Task

If you haven’t previously enabled Database Mail then you will receive a message box asking if you wish to enable the Database Mail feature. Click Yes to enable it.

Enable Database Mail

Next, on the New Profile screen, provide an e-mail Profile Name and an optional description of the profile, then select the Add button to configure an SMTP Account.

    
Create Profile Name and SMTP Account

On the New Database Mail Account screen, provide the required information for the outgoing mail server (SMTP). Fill in the Account Name, Description (optional), Email Address, Display name, Reply Email (optional), and SMTP Server Name and Port number. Then select the appropriate SMTP authentication for your server and click Next to continue.

Gmail Users

If you do not have access to an SMTP server, consider using your Gmail or Yahoo! account. Note that if you are using Gmail as your outgoing mail server, the server name will be smtp.gmail.comand the port number to be used is 587 and not the default port number 25. Also, Gmail requires a secure connection so click the check-box This server requires a secure connection (SSL).

    
Configure SMTP Account

This will return you to the New Profile screen. Click Next to continue.

    
Return to the New Profile Screen

The Manage Profile Security screen allows you to set this profile as either public or private. A private profile is accessible only to specific users or roles. A public profile allows any user or role with access to the mail host database (msdb) to send e-mail using this profile. Note that in order to send Database Mail, users must also be a added to the DatabaseMailUsersRole database role in the msdb database.

You can also specify that this profile is a default profile. In this case, users or roles can send e-mail using the profile without explicitly specifying the profile name. If the user or role sending the e-mail message has a default private profile (use the Private Profiles tab on this screen), Database Mail uses that profile. If the user or role has no default private profile, sp_send_dbmail uses the default public profile for the msdb database. If there is no default private profile for the user or role and no default public profile for the database,sp_send_dbmail returns an error. Only one profile can be marked as the default profile.

Click Next to continue.

    
Manage Profile Security

Use the Configure System Parameters screen to specify Database Mail system parameters such as retry attempts or types of attachments that you do not want to send out by including the extensions of the files (Prohibited Attachment File Extensions). Click Next to continue.

    
Configure Database Mail System Parameters

Finally, review the list of steps the wizard is about to perform, and then click Finish to execute these actions.

    
Verify Actions to Perform

Verify that all actions completed successfully. Click Close to exit the Database Mail Configuration Wizard.

    
Verify All Actions Completed Successfully

Perform a quick test of the new e-mail Profile within SQL Server Management Studio. Right-click Database Mail and select Send Test E-Mail….

Send Test E-Mail

Supply a recipient email address and click Send Test E-Mail.

Supply Recipient Email Address

Check that you received the test email and click Ok to close the dialog box; otherwise click the Troubleshoot button to investigate any errors. In addition to the Troubleshooting button, you can also review the contents of msdb.dbo.sysmail_event_log.

SELECT * FROM msdb.dbo.sysmail_event_log;
Go
Verify Email was Sent and Received

Configure Database Mail – (Transact-SQL)

The previous section included detailed instructions for setting up Database Mail using the Database Mail Configuration Wizard. Database Mail can also be configured using Transact-SQL (Database Mail stored procedures) to create profiles and accounts.

To create a Database Mail profile using Transact-SQL:

  1. Create one or more Database Mail accounts for the profile using the msdb.dbo.sysmail_add_account_sp stored procedure.
  2. Execute the stored procedure msdb.dbo.sysmail_add_profile_sp to create the profile, specifying:
    • The name of the profile to create.
    • An optional description of the profile.
  3. For each account, execute msdb.dbo.sysmail_add_profileaccount_sp to add the account to the profile.
  4. For each database role or user that will send mail using this profile, grant access to the profile by executing msdb.sysmail_add_principalprofile_sp (next section).

Example

The following example creates a Database Mail account and a Database Mail profile using Transact-SQL. The example then adds the account to the profile. Granting access to the profile to the AppUser database user will be performed in the section «Handling Security in Database Mail«.

-- Create a Database Mail account
EXEC msdb.dbo.sysmail_add_account_sp
    @account_name = 'iDevelopment SMTP Account'
  , @description = 'Administrative SMTP account for iDevelopment.info'
  , @email_address = 'dba@idevelopment.info'
  , @display_name = 'iDevelopment.info Database Support'
  , @replyto_address = 'dba@idevelopment.info'
  , @mailserver_type = 'SMTP'
  , @mailserver_name = 'relay.pair.com'
  , @port = 25
  , @use_default_credentials = 0
  , @username = 'jhunter'
  , @password = 'xxxxxxxx'
Go

-- Create a Database Mail profile
EXEC msdb.dbo.sysmail_add_profile_sp
    @profile_name = 'iDevelopment.info Profile'
  , @description = 'Administrative profile for iDevelopment.info'
Go

-- Add the account to the profile
EXEC msdb.dbo.sysmail_add_profileaccount_sp
    @profile_name = 'iDevelopment.info Profile'
  , @account_name = 'iDevelopment SMTP Account'
  , @sequence_number = 1
Go

Handling Security in Database Mail

This section provides some helpful tips when configuring security in Database Mail. At the end of this section is an example T-SQL script that explains the necessary privileges required to use Database Mail.

The DatabaseMailUserRole Role

Database Mail is guarded by the database role DatabaseMailUserRole in the msdb database in order to prevent anyone from sending arbitrary emails. Database users or roles must be created in the msdb database and must also be a member ofDatabaseMailUserRole in order to send emails with the exception of sysadmin who has all privileges. For example, the following error message will be thrown if an unauthorized user attempts to send an e-mail message using sp_send_dbmail:

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'iDevelopment.info Profile'
  , @recipients = 'dba@idevelopment.info'
  , @subject = 'Automated Test Results (Successful)'
  , @body = 'The stored procedure finished successfully.'
Go

Msg 229, Level 14, State 5, Procedure sp_send_dbmail, Line 1
The EXECUTE permission was denied on the object 'sp_send_dbmail', database 'msdb', schema 'dbo'.

In addition to sp_send_dbmail, the following Database Mail stored procedures and views will be inaccessible if the user is not a member of DatabaseMailUserRole:

   sysmail_help_status_sp
   sysmail_delete_mailitems_sp
   sysmail_allitems
   sysmail_event_log
   sysmail_faileditems
   sysmail_mailattachments
   sysmail_sentitems
   sysmail_unsentitems

Profile Security

Execute permissions for sp_send_dbmail default to all members of the DatabaseMailUserRole database role in the msdb database. However, when the user sending the message does not have permission to use the profile for the request, sp_send_dbmail returns an error and does not send the message.

A database user or role must be granted permission to use a Database Mail profile if that profile is private. Since Database Mail enforces security for mail profiles, you must explicitly grant permissions to each msdb database user or role that should have access to a private Database Mail profile. You can grant access to either specific users or to all users in msdb. A private profile restricts access to a specified list of users or roles of the mail-host database (msdb). A public profile, on the other hand, allows any user or role with access to the mail host database (msdb) to send e-mail using that profile. The Database Mail profile used in this guide is a private profile and will therefore require permissions to be granted to a user or role before it can be used to send e-mail.

Create Example User for Database Mail

The following T-SQL creates a new login named [IDEVELOPMENT\AppUser] which will be used in this guide to demonstrate sending messages using Database Mail. AppUser is an Active Directory user who will be authenticated to SQL Server using Windows Authentication. In order to use Database Mail, a new user will be created in the msdb database and granted permission to connect for the AppUser login. As already mentioned in this section, the user must be a member of DatabaseMailUserRole in order to send e-mail messages which can be performed using sp_addrolemember. Next, since the Database Mail profile being used in this guide (iDevelopment.info Profile) is a private profile, the new user will need to be granted permissions before the profile can be used. This is performed using msdb.dbo.sysmail_add_principalprofile_sp. In the example below, I will also use @is_default = 1 when calling msdb.dbo.sysmail_add_principalprofile_sp so this profile will be the default private profile for the new user. Note that each user or role may have only one default profile. Finally, in order to access other database resources used in the guide, a new user will be created in the AppDB and AdventureWorks2008R2 user databases along with the required permissions.

USE [master]
Go

-- Create a login account to use Windows Authentication
CREATE LOGIN [IDEVELOPMENT\AppUser] FROM WINDOWS WITH
    DEFAULT_DATABASE = [AppDB];
Go

--------------------------------------------------------------

-- Create a user in the [msdb] database

USE [msdb]
Go

CREATE USER [AppUser] FOR LOGIN [IDEVELOPMENT\AppUser];
Go

GRANT CONNECT TO [AppUser];
Go

--------------------------------------------------------------

USE [msdb]
Go

-- Add user to the Database Mail role
EXEC sp_addrolemember
    @rolename = 'DatabaseMailUserRole'
  , @membername = 'AppUser';
Go

-- Grants permission for a database user or role
-- to use a private Database Mail profile
EXEC msdb.dbo.sysmail_add_principalprofile_sp
    @profile_name = 'iDevelopment.info Profile'
  , @principal_name = 'AppUser'
  , @is_default = 1;
Go

--------------------------------------------------------------

-- Create a user in the [AppDB] database

USE [AppDB]
Go

CREATE USER [AppUser] FOR LOGIN [IDEVELOPMENT\AppUser];
Go

GRANT CONNECT TO [AppUser];
Go

--------------------------------------------------------------

-- Create a user in the [AdventureWorks2008R2] database

USE [AdventureWorks2008R2]
Go

CREATE USER [AppUser] FOR LOGIN [IDEVELOPMENT\AppUser];
Go

GRANT CONNECT TO [AppUser];
Go

GRANT SELECT ON AdventureWorks2008R2.Production.WorkOrder TO [AppUser];
Go

GRANT SELECT ON AdventureWorks2008R2.Production.Product TO [AppUser];
Go

Send E-Mail from Database Mail

This section provides several examples of how to send messages using Database Mail. The user sending the messages will be AppUser. This user has the necessary privileges to send messages from Database Mail as described in the section Handling Security in Database Mail.

The examples presented in this section assume that the Database Mail e-mail profile iDevelopment.info Profile has been created as demonstrated earlier in this guide. This profile tells Database Mail which account it should use to send e-mails. In each of the examples presented, I will be providing the @profile parameter to the sp_send_dbmail system procedure to include the iDevelopment.info Profile. When profile is not specified, sp_send_dbmail uses a default profile. If the user sending the e-mail message has a default private profile, Database Mail uses that profile. If the user has no default private profile, sp_send_dbmail uses the default public profile. If there is no default private profile for the user and no default public profile, sp_send_dbmail returns an error.

When sending messages, sp_send_dbmail does not support e-mail messages with no content. To send an e-mail message, you must specify at least one of @body, @query, @file_attachments, or @subject. Otherwise, sp_send_dbmail returns an error.

Send a Simple Text E-Mail Message

This example sends an e-mail message to the Database Administration Team using the e-mail address dba@idevelopment.info. The message has the subject 'Automated Test Results (Successful)'. The body of the message contains the text 'The stored procedure finished successfully.'. Notice that I can be connected to any database and that the sp_send_dbmail stored procedure is in the msdb database.

USE AppDB
Go

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'iDevelopment.info Profile'
  , @recipients = 'dba@idevelopment.info'
  , @subject = 'Automated Test Results (Successful)'
  , @body = 'The stored procedure finished successfully.';
Go

Send an E-Mail Message with the Results of a Query

This example sends an e-mail message to the Database Administration Team using the e-mail address dba@idevelopment.info. The message has the subject 'Work Order Count', and executes a query that shows the number of work orders with a DueDateless than two days after April 30, 2006. Database Mail attaches the result as a text file by using the @attach_query_result_as_file = 1 parameter.

USE AdventureWorks2008R2
Go

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'iDevelopment.info Profile'
  , @recipients = 'dba@idevelopment.info'
  , @subject = 'Work Order Count'
  , @query = 'SELECT COUNT(*) AS "Number of Work Orders"
              FROM AdventureWorks2008R2.Production.WorkOrder
              WHERE DueDate > ''2006-04-30''
                AND DATEDIFF(dd, ''2006-04-30'', DueDate) < 2'
  , @attach_query_result_as_file = 1
  , @query_attachment_filename = 'Work Order Count.txt';
Go
  • @query

    The query parameter is of type nvarchar(max) and can contain any valid Transact-SQL statements. Because the query is actually executed in a separate session, local variables in the script calling sp_send_dbmail are not available to the query.

  • @attach_query_result_as_file

    Use attach_query_result_as_file to control whether the results of the query will be attached as a file, or included in the body of the e-mail message. attach_query_result_as_file is of type bit, with a default of 0. When the value is 0, the query results are included in the body of the e-mail message, after the contents of the @body parameter. When the value is 1, the results are returned as an attachment. This parameter is only applicable if @query is specified.

  • @query_attachment_filename

    query_attachment_filename specifies the file name to use for the result set of the query attachment. query_attachment_filename is of type nvarchar(255), with a default of NULL. This parameter is ignored when attach_query_result is 0. Whenattach_query_result is 1 and this parameter is NULL, Database Mail creates an arbitrary filename.

  • @query_result_width

    query_result_width is the line width, in characters, to use for formatting the results of the query. The query_result_width is of type int, with a default of 256. The value provided must be between 10 and 32767. This parameter is only applicable if@query is specified.

  • @query_result_separator

    query_result_separator is the character used to separate columns in the query output. The separator is of type char(1). Defaults to ' ' (space).

  • @exclude_query_output

    exclude_query_output specifies whether to return the output of the query execution in the e-mail message. exclude_query_output is bit, with a default of 0. When this parameter is 0, the execution of the sp_send_dbmail stored procedure prints the message returned as the result of the query execution on the console. When this parameter is 1, the execution of the sp_send_dbmail stored procedure does not print any of the query execution messages on the console.

  • @append_query_error

    append_query_error specifies whether to send the e-mail when an error returns from the query specified in the @query argument. append_query_error is bit, with a default of 0. When this parameter is 1, Database Mail sends the e-mail message and includes the query error message in the body of the e-mail message. When this parameter is 0, Database Mail does not send the e-mail message, and sp_send_dbmail ends with return code 1, indicating failure.

  • @query_no_truncate

    query_no_truncate specifies whether to execute the query with the option that avoids truncation of large variable length data types (varchar(max), nvarchar(max), varbinary(max), xml, text, ntext, image, and user-defined data types). When set, query results do not include column headers. The query_no_truncate value is of type bit. When the value is 0 or not specified, columns in the query truncate to 256 characters. When the value is 1, columns in the query are not truncated. This parameter defaults to 0.

    When used with large amounts of data, the @query_no_truncate option consumes additional resources and can slow server performance.
  • @query_result_no_padding

    query_result_no_padding is of type bit. The default is 0. When you set to 1, the query results are not padded, possibly reducing the file size.If you set @query_result_no_padding to 1 and you set the @query_result_width parameter, the@query_result_no_padding parameter overwrites the @query_result_width parameter. In this case no error occurs. If you set the @query_result_no_padding to 1 and you set the @query_no_truncate parameter, an error is raised.

Send an HTML E-Mail Message

This example sends an e-mail message to the Database Administration Team using the e-mail address dba@idevelopment.info. The message has the subject 'Work Order List', and contains an HTML document that shows the work orders with a DueDateless than two days after April 30, 2006. Database Mail sends the message in HTML format using @body_format = 'HTML'. body_format is of type varchar(20) and specifies the format of the message body. Valid values are TEXT (default) and HTML. The default value is NULL which defaults to TEXT. When specified, the headers of the outgoing message are set to indicate that the message body has the specified format.

USE AdventureWorks2008R2
Go

DECLARE @tableHTML  NVARCHAR(MAX);

SET @tableHTML =
    N'<h1>Work Order Report</h1>'
  + N'<table border="1">'
  + N'<tr><th>Work Order ID</th><th>Product ID</th>'
  + N'<th>Name</th><th>Order Qty</th><th>Due Date</th>'
  + N'<th>Expected Revenue</th></tr>'
  + CAST ( (  SELECT
                  td = wo.WorkOrderID , ''
                , td = p.ProductID    , ''
                , td = p.Name         , ''
                , td = wo.OrderQty    , ''
                , td = wo.DueDate     , ''
                , td = (p.ListPrice - p.StandardCost) * wo.OrderQty
              FROM AdventureWorks2008R2.Production.WorkOrder AS wo
                     JOIN AdventureWorks2008R2.Production.Product AS p
                     ON wo.ProductID = p.ProductID
              WHERE
                    DueDate > '2006-04-30'
                AND DATEDIFF(dd, '2006-04-30', DueDate) < 2
              ORDER BY
                  DueDate ASC
                , (p.ListPrice - p.StandardCost) * wo.OrderQty DESC
              FOR XML PATH('tr'), TYPE
           ) AS NVARCHAR(MAX)
         )
  + N'</table>';

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'iDevelopment.info Profile'
  , @recipients = 'dba@idevelopment.info'
  , @subject = 'Work Order List'
  , @body = @tableHTML
  , @body_format = 'HTML';
Go

Send an E-Mail Message with File Attachment

Database Mail uses the Microsoft Windows security context of the current user to control access to files. Therefore, users who are authenticated with SQL Server Authentication cannot attach files using @file_attachments. Windows does not allow SQL Server to provide credentials from a remote computer to another remote computer. Therefore, Database Mail may not be able to attach files from a network share in cases where the command is run from a computer other than the computer that SQL Server runs on. Attempting to attach files using file_attachments while authenticated with SQL Server Authentication will result in the following error:

Msg 22051, Level 16, State 1, Line 0
The client connection security context could not be impersonated.
Attaching files require an integrated client login

The following example demonstrates how to send multiple file attachments. When sending multiple attachments, each file attachment is separated by a semicolon (;). Space are allowed in the file path so long as the semicolon can delimit each path in the list.

USE AppDB
Go

EXEC msdb.dbo.sp_send_dbmail
    @profile_name = 'iDevelopment.info Profile'
  , @recipients = 'dba@idevelopment.info'
  , @subject = 'Attach Local File'
  , @body = 'This is a test message to verify that
Database Mail can attach multiple files from the local file system.'
  , @file_attachments = 'U:\MSSQL\Files\linux-logo.png;U:\MSSQL\Files\SQLTestMessage.txt';
Go

By default, Database Mail limits file attachments to 1 MB per file. Changing this limit, as well as modifying other Database Mail system parameters can be performed using the Database Mail Configuration Wizard. To start the Database Mail Configuration Wizard, inObject Explorer, expand the Management folder in SQL Server Management Studio, right-click Database Mail, and select Configure Database Mail. Select View or change system parameters and click Next to continue.

    
Database Mail Configuration Wizard

The file attachment limitation can be modified using the Maximum File Size (Bytes) system parameter. From the Configure System Parameters screen you can also specify types of attachments that you do not want to send out by including the extensions of the files for the Prohibited Attachment File Extensions system parameter.

    
Configure System Parameters Screen

Configure Email Notification for SQL Server Agent

After completing the setup and testing of Database Mail, you can optionally select it as your mail system for the SQL Server agent to send e-mail notifications. This can be configured by launching SQL Server Management Studio, right-click the SQL Server Agent node in Object Explorer and select Properties.

SQL Server Agent Properties

Select the Alert System page in the SQL Server Agent Properties dialog. Check the Enable mail profile option and choose Database Mail in the Mail system pull-down menu. Next, verify the correct Mail profile is selected.

    
SQL Server Agent Properties | Alert System

After configuring the alert system, restart the SQL Server Agent service. If you experience any problems sending e-mail notifications through the SQL Server Agent, check the service account that the agent is running under. If the SQL Server Agent is running with one of the built-in accounts like the Local System account, resources outside the SQL Server machine will be unavailable. This includes SMTP mail servers that are on other machines. If this is the case, change the service account for the SQL Server Agent to a domain account to resolve this issue.

Managing Database Mail

How to Start and Stop Database Mail

Use the msdb.dbo.sysmail_stop_sp system procedure to stop Database Mail. This will stop the Service Broker objects that the external program uses. sp_send_dbmail still accepts mail when Database Mail is stopped using sysmail_stop_sp.

EXEC msdb.dbo.sysmail_stop_sp;
Go

To start Database Mail, use msdb.dbo.sysmail_start_sp.

EXEC msdb.dbo.sysmail_start_sp;
Go

The Database Mail External Program is activated when there are e-mail messages to be processed. When there have been no messages to send for the specified time-out period, the program exits. To confirm the Database Mail activation is started, execute the following statement.

EXEC msdb.dbo.sysmail_help_status_sp;
Go

Status
-------
STARTED

If the Database Mail external program is started, check the status of the mail queue with the following statement:

EXEC msdb.dbo.sysmail_help_queue_sp @queue_type = 'mail';
Go

queue_type length  state               last_empty_rowset_time  last_activated_time
---------- ------- ------------------- ----------------------- -----------------------
mail       0       RECEIVES_OCCURRING  2011-04-19 19:41:20.323 2011-04-19 19:40:17.787

The mail queue should have the state of RECEIVES_OCCURRING. The status queue may vary from moment to moment. If the mail queue state is not RECEIVES_OCCURRING, try stopping the queue using sysmail_stop_sp and then starting the queue usingsysmail_start_sp.

Archive Database Mail Messages and Event Logs

As mentioned throughout this guide, copies of Database Mail messages and their attachments are retained in msdb tables along with the Database Mail event log. Periodically you might want to reduce the size of the tables and remove messages and events that are no longer needed. The following procedures create a SQL Server Agent job to automate that process.

  1. The first procedure creates a job named Archive Database Mail with five steps. The job will be run as SQLServerDBA who is the owner of the Database Mail Archive database (DBMailArchive) and has sysadmin privileges.
  2. The first step copies all messages from the Database Mail tables in msdb to a new table in the Database Mail Archive database and named after the previous month in the format DBMailArchive_Messages_<year_month>. For example:
    DBMailArchive.dbo.DBMailArchive_Messages_2011_3
  3. The second step copies the attachments related to the messages copied in the first step, from the Database Mail tables in msdb to a new table in the Database Mail Archive database and named after the previous month in the formatDBMailArchive_Attachments_<year_month>. For example:
    DBMailArchive.dbo.DBMailArchive_Attachments_2011_3
  4. The third step copies the events from the Database Mail event log that are related to the messages copied in the first step, from the Database Mail tables in msdb to a new table in the Database Mail Archive database and named after the previous month in the format DBMailArchive_Event_Log_<year_month>. For example:
    DBMailArchive.dbo.DBMailArchive_Event_Log_2011_3
  5. The fourth step deletes the records of the transferred mail items from the Database Mail tables in msdb using the msdb.dbo.sysmail_delete_mailitems_sp stored procedure.
  6. The fifth step deletes the events related to the transferred mail items from the Database Mail event log table in msdb using the msdb.dbo.sysmail_delete_log_sp stored procedure.
  7. The final procedure schedules the job to run at the start of each month.

For this example, the archive tables will be created in a new database named DBMailArchive owned by SQLServerDBA. The old messages, attachments, and log entries will be moved from the base tables in msdb to the archive tables. When the job completes, an e-mail message will be sent to operators with the status of the run. For production use, you might want to consider more robust error checking. Also, if the archived messages are not required to be stored in a database, they can be exported to a text file, or just deleted.

Create a SQL Server Agent Job

  1. In Object Explorer, expand SQL Server Agent, right-click Jobs, and then click New Job.
  2. In the New Job dialog box, in the Name box, type Archive Database Mail.
  3. In the Owner box, confirm that the job owner is a member of the sysadmin fixed server role. The job owner in this example will be SQLServerDBA who is the owner of the DBMailArchive database and a member of the sysadmin fixed server role.
  4. In the Category box, click the Database Maintenance.
  5. In the Description box, type Archive Database Mail messages, and then click Steps.

Create a Step to Archive the Database Mail Messages

  1. On the Steps page, click New.
  2. In the Step name box, type Copy Database Mail Items.
  3. In the Type box, select Transact-SQL script (T-SQL).
  4. In the Database box, select msdb.
  5. In the Command box, type the following statement to create a table named after the previous month, containing rows older than the start of the current month:
    DECLARE @ArchiveDatabaseName NVARCHAR(30) = 'DBMailArchive';
    DECLARE @ArchiveSchemaName NVARCHAR(30) = 'dbo';
    DECLARE @LastMonth NVARCHAR(12);
    DECLARE @CopyDate NVARCHAR(20);
    DECLARE @CreateTable NVARCHAR(250);
    
    SET @LastMonth = (SELECT CAST(DATEPART(yyyy,GETDATE()) AS CHAR(4)) + '_' + CAST(DATEPART(mm,GETDATE())-1 AS VARCHAR(2)));
    SET @CopyDate = (SELECT CAST(CONVERT(CHAR(8), CURRENT_TIMESTAMP - DATEPART(dd,GETDATE()-1), 112) AS DATETIME));
    SET @CreateTable = 'SELECT * INTO ' + @ArchiveDatabaseName + '.' + @ArchiveSchemaName +
                       '.[' + @ArchiveDatabaseName + '_Messages_' +
                       @LastMonth +
                       '] FROM sysmail_allitems WHERE send_request_date < ''' +
                       @CopyDate +
                       ''';';
    
    EXEC sp_executesql @CreateTable;
  6. Click OK to save the step.

Create a Step to Archive the Database Mail Attachments

  1. On the Steps page, click New.
  2. In the Step name box, type Copy Database Mail Attachments.
  3. In the Type box, select Transact-SQL script (T-SQL).
  4. In the Database box, select msdb.
  5. In the Command box, type the following statement to create an attachments table named after the previous month, containing the attachments that correspond to the messages transferred in the previous step:
    DECLARE @ArchiveDatabaseName NVARCHAR(30) = 'DBMailArchive';
    DECLARE @ArchiveSchemaName NVARCHAR(30) = 'dbo';
    DECLARE @LastMonth NVARCHAR(12);
    DECLARE @CopyDate NVARCHAR(20);
    DECLARE @CreateTable NVARCHAR(250);
    
    SET @LastMonth = (SELECT CAST(DATEPART(yyyy,GETDATE()) AS CHAR(4)) + '_' + CAST(DATEPART(mm,GETDATE())-1 AS VARCHAR(2)));
    SET @CopyDate = (SELECT CAST(CONVERT(CHAR(8), CURRENT_TIMESTAMP - DATEPART(dd,GETDATE()-1), 112) AS DATETIME));
    SET @CreateTable = 'SELECT * INTO ' + @ArchiveDatabaseName + '.' + @ArchiveSchemaName +
                       '.[' + @ArchiveDatabaseName + '_Attachments_' +
                       @LastMonth +
                       '] FROM sysmail_attachments WHERE mailitem_id IN (SELECT DISTINCT mailitem_id FROM ' +
                       @ArchiveDatabaseName + '.' + @ArchiveSchemaName + '.[' + @ArchiveDatabaseName + '_Messages_' +
                       @LastMonth +
                       '] )';
    
    EXEC sp_executesql @CreateTable;
  6. Click OK to save the step.

Create a Step to Archive the Database Mail Log

  1. On the Steps page, click New.
  2. In the Step name box, type Copy Database Mail Log.
  3. In the Type box, select Transact-SQL script (T-SQL).
  4. In the Database box, select msdb.
  5. In the Command box, type the following statement to create a log table named after the previous month, containing the log entries that correspond to the messages transferred in the earlier step:
    DECLARE @ArchiveDatabaseName NVARCHAR(30) = 'DBMailArchive';
    DECLARE @ArchiveSchemaName NVARCHAR(30) = 'dbo';
    DECLARE @LastMonth NVARCHAR(12);
    DECLARE @CopyDate NVARCHAR(20);
    DECLARE @CreateTable NVARCHAR(250);
    
    SET @LastMonth = (SELECT CAST(DATEPART(yyyy,GETDATE()) AS CHAR(4)) + '_' + CAST(DATEPART(mm,GETDATE())-1 AS VARCHAR(2)));
    SET @CopyDate = (SELECT CAST(CONVERT(CHAR(8), CURRENT_TIMESTAMP - DATEPART(dd,GETDATE()-1), 112) AS DATETIME));
    SET @CreateTable = 'SELECT * INTO ' + @ArchiveDatabaseName + '.' + @ArchiveSchemaName +
                       '.[' + @ArchiveDatabaseName + '_Event_Log_' +
                       @LastMonth +
                       '] FROM sysmail_event_log WHERE mailitem_id IN (SELECT DISTINCT mailitem_id FROM ' +
                       @ArchiveDatabaseName + '.' + @ArchiveSchemaName + '.[' + @ArchiveDatabaseName + '_Messages_' +
                       @LastMonth +
                       '] )';
    
    EXEC sp_executesql @CreateTable;
  6. Click OK to save the step.

Create a Step to Remove the Archived Rows from Database Mail

  1. On the Steps page, click New.
  2. In the Step name box, type Remove Rows from Database Mail.
  3. In the Type box, select Transact-SQL script (T-SQL).
  4. In the Database box, select msdb.
  5. In the Command box, type the following statement to remove rows older than the current month from the Database Mail tables:
    DECLARE @CopyDate NVARCHAR(20);
    
    SET @CopyDate = (SELECT CAST(CONVERT(CHAR(8), CURRENT_TIMESTAMP - DATEPART(dd,GETDATE()-1), 112) AS DATETIME));
    
    EXEC msdb.dbo.sysmail_delete_mailitems_sp @sent_before = @CopyDate;
  6. Click OK to save the step.

Create a Step to Remove the Archived Items from Database Mail Event Log

  1. On the Steps page, click New.
  2. In the Step name box, type Remove Rows from Database Mail Event Log.
  3. In the Type box, select Transact-SQL script (T-SQL).
  4. In the Database box, select msdb.
  5. In the Command box, type the following statement to remove rows older than the current month from the Database Mail event log:
    DECLARE @CopyDate NVARCHAR(20);
    
    SET @CopyDate = (SELECT CAST(CONVERT(CHAR(8), CURRENT_TIMESTAMP - DATEPART(dd,GETDATE()-1), 112) AS DATETIME));
    
    EXEC msdb.dbo.sysmail_delete_log_sp @logged_before = @CopyDate;
  6. Click OK to save the step.

Schedule the Job to Run at the Start of Each Month

  1. In the New Job dialog box, click Schedules.
  2. On the Schedules page, click New.
  3. In the Name box, type Archive Database Mail.
  4. In the Schedule type box, select Recurring.
  5. In the Frequency area, select the options to run the job on the first day of every month.
  6. In the Daily frequency area, select Occurs once at 3:00:00 AM.
  7. Verify that the other options are configured as you wish, and then click OK to save the schedule.
  8. Click OK to save the job.

MERGE (Transact-SQL)

MERGE (Transact-SQL)

SQL Server 2014
Otras versiones

Realiza operaciones de inserción, actualización o eliminación en una tabla de destino según los resultados de una combinación con una tabla de origen. Por ejemplo, puede sincronizar dos tablas insertando, actualizando o eliminando las filas de una tabla según las diferencias que se encuentren en la otra.

Se aplica a: SQL Server (desde SQL Server 2008 hasta la versión actual), Windows Azure SQL Database (desde la versión inicial hasta la versión actual).

Icono de vínculo a temasConvenciones de sintaxis de Transact-SQL

 

[ WITH <common_table_expression> [,...n] ]
MERGE 
    [ TOP ( expression ) [ PERCENT ] ] 
    [ INTO ] <target_table> [ WITH ( <merge_hint> ) ] [ [ AS ] table_alias ]
    USING <table_source> 
    ON <merge_search_condition>
    [ WHEN MATCHED [ AND <clause_search_condition> ]
        THEN <merge_matched> ] [ ...n ]
    [ WHEN NOT MATCHED [ BY TARGET ] [ AND <clause_search_condition> ]
        THEN <merge_not_matched> ]
    [ WHEN NOT MATCHED BY SOURCE [ AND <clause_search_condition> ]
        THEN <merge_matched> ] [ ...n ]
    [ <output_clause> ]
    [ OPTION ( <query_hint> [ ,...n ] ) ]    
;

<target_table> ::=
{ 
    [ database_name . schema_name . | schema_name . ]
  target_table
}

<merge_hint>::=
{
    { [ <table_hint_limited> [ ,...n ] ]
    [ [ , ] INDEX ( index_val [ ,...n ] ) ] }
}

<table_source> ::= 
{
    table_or_view_name [ [ AS ] table_alias ] [ <tablesample_clause> ] 
        [ WITH ( table_hint [ [ , ]...n ] ) ] 
  | rowset_function [ [ AS ] table_alias ] 
        [ ( bulk_column_alias [ ,...n ] ) ] 
  | user_defined_function [ [ AS ] table_alias ]
  | OPENXML <openxml_clause> 
  | derived_table [ AS ] table_alias [ ( column_alias [ ,...n ] ) ] 
  | <joined_table> 
  | <pivoted_table> 
  | <unpivoted_table> 
}

<merge_search_condition> ::=
    <search_condition>

<merge_matched>::=
    { UPDATE SET <set_clause> | DELETE }

<set_clause>::=
SET
  { column_name = { expression | DEFAULT | NULL }
  | { udt_column_name.{ { property_name = expression
                        | field_name = expression }
                        | method_name ( argument [ ,...n ] ) }
    }
  | column_name { .WRITE ( expression , @Offset , @Length ) }
  | @variable = expression
  | @variable = column = expression
  | column_name { += | -= | *= | /= | %= | &= | ^= | |= } expression
  | @variable { += | -= | *= | /= | %= | &= | ^= | |= } expression
  | @variable = column { += | -= | *= | /= | %= | &= | ^= | |= } expression
  } [ ,...n ] 

<merge_not_matched>::=
{
    INSERT [ ( column_list ) ] 
        { VALUES ( values_list )
        | DEFAULT VALUES }
}

<clause_search_condition> ::=
    <search_condition>

<search condition> ::=
    { [ NOT ] <predicate> | ( <search_condition> ) } 
    [ { AND | OR } [ NOT ] { <predicate> | ( <search_condition> ) } ] 
[ ,...n ] 

<predicate> ::= 
    { expression { = | < > | ! = | > | > = | ! > | < | < = | ! < } expression 
    | string_expression [ NOT ] LIKE string_expression 
  [ ESCAPE 'escape_character' ] 
    | expression [ NOT ] BETWEEN expression AND expression 
    | expression IS [ NOT ] NULL 
    | CONTAINS 
  ( { column | * } , '< contains_search_condition >' ) 
    | FREETEXT ( { column | * } , 'freetext_string' ) 
    | expression [ NOT ] IN ( subquery | expression [ ,...n ] ) 
    | expression { = | < > | ! = | > | > = | ! > | < | < = | ! < } 
  { ALL | SOME | ANY} ( subquery ) 
    | EXISTS ( subquery ) } 

<output_clause>::=
{
    [ OUTPUT <dml_select_list> INTO { @table_variable | output_table }
        [ (column_list) ] ]
    [ OUTPUT <dml_select_list> ]
}

<dml_select_list>::=
    { <column_name> | scalar_expression } 
        [ [AS] column_alias_identifier ] [ ,...n ]

<column_name> ::=
    { DELETED | INSERTED | from_table_name } . { * | column_name }
    | $action

WITH <common_table_expression>
Especifica la vista o el conjunto de resultados temporal indicado, que también se conoce como expresión de tabla común, definido en el ámbito de la instrucción MERGE. El conjunto de resultados se deriva de una consulta simple. La instrucción MERGE hace referencia al conjunto de resultados. Para obtener más información, vea WITH common_table_expression (Transact-SQL).

TOP ( expression ) [ PERCENT ]
Especifica el número o porcentaje de filas afectadas. expression puede ser un número o un porcentaje de filas.Las filas a las que se hace referencia en la expresión TOP no están organizadas en ningún orden. Para obtener más información, vea TOP (Transact-SQL).

La cláusula TOP se aplica después de que se combinen toda la tabla de origen y toda la tabla de destino, y se quiten las filas combinadas que no reúnan las condiciones para las acciones de inserción, actualización o eliminación. La cláusula TOP reduce aún más el número de filas combinadas al valor especificado y se aplican las acciones de inserción, actualización o eliminación a las filas combinadas restantes de una manera desordenada. Es decir, no hay ningún orden en el que las filas se distribuyan entre las acciones definidas en las cláusulas WHEN. Por ejemplo, cuando se especifica TOP (10) afecta a 10 filas; de estas filas, 7 se pueden actualizar y 3 insertar, o se pueden eliminar 1, actualizar 5 e insertar 4, etc.

Dado que la instrucción MERGE realiza un recorrido completo de ambas tablas, de destino y de origen, el rendimiento de E/S puede verse afectado al utilizar la cláusula TOP para modificar una tabla grande mediante la creación de varios lotes. En este escenario, es importante asegurase de que todos los lotes sucesivos tengan como destino nuevas filas.

database_name
Es el nombre de la base de datos donde se encuentra target_table.

schema_name
Es el nombre del esquema al que pertenece target_table.

target_table
Es la tabla o la vista con la que las filas de datos de <table_source> se hacen coincidir según la <clause_search_condition>. target_table es el destino de las operaciones de inserción, actualización o eliminación que las cláusulas WHEN de la instrucción MERGE especifican.

Si target_table es una vista, cualquier acción con ella debe satisfacer las condiciones para actualizar las vistas.Para obtener más información, vea Modificar datos mediante una vista.

target_table no puede ser una tabla remota. target_table no puede tener ninguna regla definida.

[ AS ] table_alias
Es un nombre alternativo que se utiliza para hacer referencia a una tabla.

USING <table_source>
Especifica el origen de datos que se hace coincidir con las filas de datos en target_table según <merge_search condition>. El resultado de esta coincidencia dicta las acciones que tomarán las cláusulas WHEN de la instrucción MERGE. <table_source> puede ser una tabla remota o una tabla derivada que tengan acceso a las tablas remotas.

<table_source> puede ser una tabla derivada que use el constructor con valores de tabla de Transact-SQL para construir una tabla especificando varias filas.

Para obtener más información acerca de la sintaxis y los argumentos de esta cláusula, vea FROM (Transact-SQL).

ON <merge_search_condition>
Especifica las condiciones en las que <table_source> se combina con target_table para determinar dónde coinciden.

Nota de advertenciaAdvertencia
Es importante especificar solamente las columnas de la tabla de destino que se utilizan para los propósitos de la coincidencia. Es decir, especifique las columnas de la tabla de destino que se comparan con la correspondiente columna de la tabla de origen. No intente mejorar el rendimiento de las consultas filtrando las filas de la tabla de destino en la cláusula ON, según se especifica con AND NOT target_table.column_x = value. Si se hace esto, se pueden devolver resultados inesperados e incorrectos.
WHEN MATCHED THEN <merge_matched>
Especifica que todas las filas de target_table que coinciden con las filas que devuelve <table_source> ON <merge_search_condition> y que satisfacen alguna condición de búsqueda adicional se actualizan o eliminan según la cláusula <merge_matched>.

La instrucción MERGE puede tener a lo sumo dos cláusulas WHEN MATCHED. Si se especifican dos cláusulas, la primera debe ir acompañada de una cláusula AND <search_condition>. Para una fila determinada, la segunda cláusula WHEN MATCHED se aplica solamente si no se aplica la primera. Si hay dos cláusulas WHEN MATCHED, una debe especificar una acción UPDATE y la otra una acción DELETE. Si se especifica UPDATE en la cláusula <merge_matched> y más de una fila de <table_source> coincide con una fila en target_table según la <merge_search_condition>, SQL Server devuelve un error. La instrucción MERGE no puede actualizar la misma fila más de una vez, ni actualizar o eliminar la misma fila.

WHEN NOT MATCHED [ BY TARGET ] THEN <merge_not_matched>
Especifica que una fila se inserta en target_table para cada fila que devuelve <table_source> ON <merge_search_condition> que no coincide con una fila de target_table, pero satisface una condición de búsqueda adicional, si está presente. La cláusula <merge_not_matched> especifica los valores que insertar. La instrucción MERGE puede tener solamente una cláusula WHEN NOT MATCHED.

WHEN NOT MATCHED BY SOURCE THEN <merge_matched>
Especifica que todas las filas de target_table que no coinciden con las filas que devuelve <table_source> ON <merge_search_condition> y que satisfacen alguna condición de búsqueda adicional se actualizan o eliminan según la cláusula <merge_matched>.

La instrucción MERGE puede tener a lo sumo dos cláusulas WHEN NOT MATCHED BY SOURCE. Si se especifican dos cláusulas, la primera debe ir acompañada de una cláusula AND <clause_search_condition>.Para una fila determinada, la segunda cláusula WHEN NOT MATCHED BY SOURCE se aplica solamente si no se aplica la primera. Si hay dos cláusulas WHEN NOT MATCHED BY SOURCE, una debe especificar una acción UPDATE y la otra una acción DELETE. Solamente se puede hacer referencia a las columnas de la tabla de destino en <clause_search_condition>.

Cuando <table_source> no devuelve ninguna fila, no se puede tener acceso a las columnas de la tabla de origen. Si la acción de actualización o eliminación especificada en la cláusula <merge_matched> hace referencia a las columnas de la tabla de origen, se devuelve el error 207 (nombre de columna no válido). La cláusula WHEN NOT MATCHED BY SOURCE THEN UPDATE SET TargetTable.Col1 = SourceTable.Col1puede hacer que la instrucción genere un error porque Col1 en la tabla de origen es inaccesible.

AND <clause_search_condition>
Especifica cualquier condición de búsqueda válida. Para obtener más información, vea Condiciones de búsqueda (Transact-SQL).

<table_hint_limited>
Especifica una o más sugerencias de tabla que se aplican en la tabla de destino para cada una de las acciones de inserción, actualización o eliminación que realiza la instrucción MERGE. La palabra clave WITH y los paréntesis son obligatorios.

No se permiten NOLOCK ni READUNCOMMITTED. Para obtener más información acerca de las sugerencias de tabla, vea Sugerencias de tabla (Transact-SQL).

Especificar la sugerencia TABLOCK en una tabla que es el destino de una instrucción INSERT tiene el mismo efecto que especificar la sugerencia TABLOCKX. Se realiza un bloqueo exclusivo en la tabla. Cuando se especifica FORCESEEK, se aplica a la instancia implícita de la tabla de destino combinada con la tabla de origen.

Nota de advertenciaAdvertencia
Si se especifica READPAST con WHEN NOT MATCHED [ BY TARGET ] THEN INSERT, pueden producirse operaciones INSERT que infrinjan las restricciones UNIQUE.
INDEX ( index_val [ ,…n ] )
Especifica el nombre o identificador de uno o más índices de la tabla de destino para realizar una combinación implícita con la tabla de origen. Para obtener más información, vea Sugerencias de tabla (Transact-SQL).

<output_clause>
Devuelve una fila para cada fila de target_table que se actualiza, inserta o elimina, en ningún orden en concreto. $action se puede especificar en la cláusula de salida. $action es una columna de tipo nvarchar(10)que devuelve uno de estos tres valores por cada fila: ‘INSERT’, ‘UPDATE’ o ‘DELETE’, según la acción realizada en dicha fila. Para obtener más información acerca de los argumentos de esta cláusula, vea OUTPUT (cláusula de Transact-SQL).

OPTION ( <query_hint> [ ,…n ] )
Especifica que se utilizan las sugerencias del optimizador para personalizar el modo en que el motor de base de datos procesa la instrucción. Para obtener más información, vea Sugerencias de consulta (Transact-SQL).

<merge_matched>
Especifica la acción de actualización o eliminación que se aplica a todas las filas de target_table que no coinciden con las filas que devuelve <table_source> ON <merge_search_condition>, y que satisfacen cualquier condición de búsqueda adicional.

UPDATE SET <set_clause>
Especifica la lista de nombres de columna o de variable que se van a actualizar en la tabla de destino y los valores con los que se actualizan.

Para obtener más información acerca de los argumentos de esta cláusula, vea UPDATE (Transact-SQL).No se puede establecer una variable con el mismo valor que una columna.

DELETE
Especifica que las filas coincidentes de las filas de target_table se eliminan.

<merge_not_matched>
Especifica los valores que insertar en la tabla de destino.

(column_list)
Es una lista de una o varias columnas de la tabla de destino en la que insertar los datos. Las columnas se deben especificar como un nombre de una sola parte o, de lo contrario, se producirá un error en la instrucción MERGE. column_list se debe agregar entre paréntesis y delimitarse mediante comas.

VALUES ( values_list)
Es una lista separada por comas de constantes, variables o expresiones que devuelve los valores que se insertarán en la tabla de destino. Las expresiones no pueden contener una instrucción EXECUTE.

DEFAULT VALUES
Hace que la fila insertada contenga los valores predeterminados definidos para cada columna.

Para obtener más información sobre esta cláusula, vea INSERT (Transact-SQL).

<search condition>
Especifica las condiciones de búsqueda utilizadas para especificar <merge_search_condition> o <clause_search_condition>. Para obtener más información acerca de los argumentos de esta cláusula, veaCondiciones de búsqueda (Transact-SQL).

Al menos se debe especificar una de las tres cláusulas MATCHED, pero se pueden especificar en cualquier orden. Una variable no puede actualizarse más de una vez en la misma cláusula MATCHED.

Cualquier acción de inserción, actualización o eliminación especificada en la tabla de destino por la instrucción MERGE está limitada por las restricciones definidas en ella, incluidas las restricciones de integridad referencial en cascada. Si IGNORE_DUP_KEY se establece en ON para algún índice único de la tabla de destino, MERGE omite este valor.

La instrucción MERGE requiere un punto y coma (;) como terminador. Se genera el error 10713 cuando una instrucción MERGE se ejecuta sin el terminador.

Cuando se utiliza después de MERGE, @@ROWCOUNT (Transact-SQL) devuelve el número total de filas insertadas, actualizadas y eliminadas al cliente.

MERGE es una palabra clave totalmente reservada cuando el nivel de compatibilidad de la base de datos se establece en 100 o superior. La instrucción MERGE también está disponible en los niveles de compatibilidad 90 y 100 de la base de datos; sin embargo, la palabra clave no se reserva completamente cuando el nivel de compatibilidad se establece en 90.

La instrucción MERGE no se debe usar cuando se emplea la replicación de actualización en cola. MERGE y el desencadenador de actualización en cola no son compatibles. Reemplace la instrucción MERGE con una instrucción de inserción o de actualización.

Implementación de desencadenadores

Para cada acción de inserción, actualización o eliminación especificada en la instrucción MERGE, SQL Server activa los desencadenadores AFTER correspondientes definidos en la tabla de destino, pero no garantiza qué acción activará los desencadenadores primero o último. Los desencadenadores definidos para la misma acción cumplen el orden que especifique. Para obtener más información sobre cómo establecer el orden de activación de los desencadenadores, vea Especificar el primer y el último desencadenador.

Si la tabla de destino tiene habilitado un desencadenador INSTEAD OF definido en ella para una acción de inserción, actualización o eliminación realizada por una instrucción MERGE, debe tener habilitado un desencadenador INSTEAD OF para todas las acciones especificadas en la instrucción MERGE.

Si hay desencadenadores INSTEAD OF UPDATE o INSTEAD OF DELETE definidos en target_table, las operaciones de actualización o eliminación no se realizan. En su lugar, se activan los desencadenadores y las tablas inserted ydeleted se rellenan en consecuencia.

Si hay definidos desencadenadores INSTED OF INSERT en target_table, la operación de inserción no se realiza. En su lugar, se activan los desencadenadores y la tabla inserted se rellena en consecuencia.

Requiere el permiso SELECT en la tabla de origen y los permisos INSERT, UPDATE o DELETE en la tabla de destino.Para obtener información adicional, consulte la sección Permisos de los temas SELECT, INSERT, UPDATE o DELETE.

 

A.Usar MERGE para realizar operaciones INSERT y UPDATE en una tabla en una sola instrucción

Un escenario común es la actualización de una o varias columnas de una tabla si una fila coincidente existe, o la inserción de datos como una fila nueva si no existe ninguna fila coincidente. Normalmente, para hacer esto se pasan los parámetros a un procedimiento almacenado que contiene las instrucciones INSERT y UPDATE adecuadas. Con la instrucción MERGE puede realizar ambas tareas en una sola instrucción. En el ejemplo siguiente se muestra un procedimiento almacenado de la base de datos AdventureWorks2012 que contiene una instrucción INSERT y una instrucción UPDATE. A continuación, el procedimiento se modifica para realizar las operaciones equivalentes utilizando una sola instrucción MERGE.

CREATE PROCEDURE dbo.InsertUnitMeasure
    @UnitMeasureCode nchar(3),
    @Name nvarchar(25)
AS 
BEGIN
    SET NOCOUNT ON;
-- Update the row if it exists.    
    UPDATE Production.UnitMeasure
SET Name = @Name
WHERE UnitMeasureCode = @UnitMeasureCode
-- Insert the row if the UPDATE statement failed.
IF (@@ROWCOUNT = 0 )
BEGIN
    INSERT INTO Production.UnitMeasure (UnitMeasureCode, Name)
    VALUES (@UnitMeasureCode, @Name)
END
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Test Value';
SELECT UnitMeasureCode, Name FROM Production.UnitMeasure
WHERE UnitMeasureCode = 'ABC';
GO

-- Rewrite the procedure to perform the same operations using the MERGE statement.
-- Create a temporary table to hold the updated or inserted values from the OUTPUT clause.
CREATE TABLE #MyTempTable
    (ExistingCode nchar(3),
     ExistingName nvarchar(50),
     ExistingDate datetime,
     ActionTaken nvarchar(10),
     NewCode nchar(3),
     NewName nvarchar(50),
     NewDate datetime
    );
GO
ALTER PROCEDURE dbo.InsertUnitMeasure
    @UnitMeasureCode nchar(3),
    @Name nvarchar(25)
AS 
BEGIN
    SET NOCOUNT ON;

    MERGE Production.UnitMeasure AS target
    USING (SELECT @UnitMeasureCode, @Name) AS source (UnitMeasureCode, Name)
    ON (target.UnitMeasureCode = source.UnitMeasureCode)
    WHEN MATCHED THEN 
        UPDATE SET Name = source.Name
WHEN NOT MATCHED THEN
    INSERT (UnitMeasureCode, Name)
    VALUES (source.UnitMeasureCode, source.Name)
    OUTPUT deleted.*, $action, inserted.* INTO #MyTempTable;
END;
GO
-- Test the procedure and return the results.
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'New Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'XYZ', @Name = 'Test Value';
EXEC InsertUnitMeasure @UnitMeasureCode = 'ABC', @Name = 'Another Test Value';

SELECT * FROM #MyTempTable;
-- Cleanup 
DELETE FROM Production.UnitMeasure WHERE UnitMeasureCode IN ('ABC','XYZ');
DROP TABLE #MyTempTable;
GO

B.Usar MERGE para realizar operaciones UPDATE y DELETE en una tabla en una sola instrucción

En el siguiente ejemplo se usa MERGE para actualizar diariamente la tabla ProductInventory de la base de datos de ejemplo AdventureWorks2012, en función de los pedidos procesados en la tabla SalesOrderDetail. La columnaQuantity de la tabla ProductInventory se actualiza restando el número de pedidos realizados cada día para cada producto de la tabla SalesOrderDetail. Si el número de pedidos de un producto baja el nivel de inventario del mismo hasta 0 o un valor menor, la fila correspondiente a ese producto se elimina de la tabla ProductInventory.

IF OBJECT_ID (N'Production.usp_UpdateInventory', N'P') IS NOT NULL DROP PROCEDURE Production.usp_UpdateInventory;
GO
CREATE PROCEDURE Production.usp_UpdateInventory
    @OrderDate datetime
AS
MERGE Production.ProductInventory AS target
USING (SELECT ProductID, SUM(OrderQty) FROM Sales.SalesOrderDetail AS sod
    JOIN Sales.SalesOrderHeader AS soh
    ON sod.SalesOrderID = soh.SalesOrderID
    AND soh.OrderDate = @OrderDate
    GROUP BY ProductID) AS source (ProductID, OrderQty)
ON (target.ProductID = source.ProductID)
WHEN MATCHED AND target.Quantity - source.OrderQty <= 0
    THEN DELETE
WHEN MATCHED 
    THEN UPDATE SET target.Quantity = target.Quantity - source.OrderQty, 
                    target.ModifiedDate = GETDATE()
OUTPUT $action, Inserted.ProductID, Inserted.Quantity, Inserted.ModifiedDate, Deleted.ProductID,
    Deleted.Quantity, Deleted.ModifiedDate;
GO

EXECUTE Production.usp_UpdateInventory '20030501'

C.Usar MERGE para realizar operaciones INSERT y UPDATE en una tabla de destino mediante una tabla de origen derivada

En el ejemplo siguiente se usa MERGE para modificar la tabla SalesReason de la base de datos AdventureWorks2012, actualizando o insertando las filas. Cuando el valor de NewName de la tabla de origen coincide con un valor de la columna Name de la tabla de destino, (SalesReason), la columna ReasonType se actualiza en la tabla de destino. Cuando el valor de NewName no coincide, la fila del origen se inserta en la tabla de destino. La tabla de origen es una tabla derivada que usa la característica de constructor con valores de tabla de Transact-SQL para especificar varias filas en la tabla de origen. Para obtener más información acerca de cómo usar el constructor de valores de tabla en una tabla derivada, vea Constructor con valores de tabla (Transact-SQL). El ejemplo también muestra cómo almacenar los resultados de la cláusula OUTPUT en una variable de tabla y, a continuación, resumir los resultados de la instrucción MERGE realizando una sencilla operación SELECT que devuelve el recuento de las filas insertadas y actualizadas.

-- Create a temporary table variable to hold the output actions.
DECLARE @SummaryOfChanges TABLE(Change VARCHAR(20));

MERGE INTO Sales.SalesReason AS Target
USING (VALUES ('Recommendation','Other'), ('Review', 'Marketing'), ('Internet', 'Promotion'))
       AS Source (NewName, NewReasonType)
ON Target.Name = Source.NewName
WHEN MATCHED THEN
UPDATE SET ReasonType = Source.NewReasonType
WHEN NOT MATCHED BY TARGET THEN
INSERT (Name, ReasonType) VALUES (NewName, NewReasonType)
OUTPUT $action INTO @SummaryOfChanges;

-- Query the results of the table variable.
SELECT Change, COUNT(*) AS CountPerChange
FROM @SummaryOfChanges
GROUP BY Change;

D.Insertar los resultados de la instrucción MERGE en otra tabla

En el ejemplo siguiente se capturan los datos devueltos por la cláusula OUTPUT de una instrucción MERGE y se insertan en otra tabla. La instrucción MERGE actualiza diariamente la columna Quantity de la tablaProductInventory de la base de datos AdventureWorks2012, en función de los pedidos procesados en la tablaSalesOrderDetail. En el ejemplo se capturan las filas actualizadas y se insertan en otra tabla que se usa para realizar el seguimiento de los cambios del inventario.

CREATE TABLE Production.UpdatedInventory
    (ProductID INT NOT NULL, LocationID int, NewQty int, PreviousQty int,
     CONSTRAINT PK_Inventory PRIMARY KEY CLUSTERED (ProductID, LocationID));
GO
INSERT INTO Production.UpdatedInventory
SELECT ProductID, LocationID, NewQty, PreviousQty 
FROM
(    MERGE Production.ProductInventory AS pi
     USING (SELECT ProductID, SUM(OrderQty) 
            FROM Sales.SalesOrderDetail AS sod
            JOIN Sales.SalesOrderHeader AS soh
            ON sod.SalesOrderID = soh.SalesOrderID
            AND soh.OrderDate BETWEEN '20030701' AND '20030731'
            GROUP BY ProductID) AS src (ProductID, OrderQty)
     ON pi.ProductID = src.ProductID
    WHEN MATCHED AND pi.Quantity - src.OrderQty >= 0 
        THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty
    WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0 
        THEN DELETE
    OUTPUT $action, Inserted.ProductID, Inserted.LocationID, Inserted.Quantity AS NewQty, Deleted.Quantity AS PreviousQty)
 AS Changes (Action, ProductID, LocationID, NewQty, PreviousQty) WHERE Action = 'UPDATE';
GO

LISTAR TRIGGERS

SELECT
sysobjects.name AS trigger_name
,USER_NAME(sysobjects.uid) AS trigger_owner
,s.name AS table_schema
,OBJECT_NAME(parent_obj) AS table_name
,OBJECTPROPERTY( id, 'ExecIsUpdateTrigger') AS isupdate
,OBJECTPROPERTY( id, 'ExecIsDeleteTrigger') AS isdelete
,OBJECTPROPERTY( id, 'ExecIsInsertTrigger') AS isinsert
,OBJECTPROPERTY( id, 'ExecIsAfterTrigger') AS isafter
,OBJECTPROPERTY( id, 'ExecIsInsteadOfTrigger') AS isinsteadof
,OBJECTPROPERTY(id, 'ExecIsTriggerDisabled') AS [disabled]
FROM sysobjects

INNER JOIN sysusers
ON sysobjects.uid = sysusers.uid

INNER JOIN sys.tables t
ON sysobjects.parent_obj = t.object_id

INNER JOIN sys.schemas s
ON t.schema_id = s.schema_id

WHERE sysobjects.type = 'TR'