子查询在SQL Server 2005触发器中返回了多个值[2]

时间:2011-09-20 21:09:25

标签: sql-server sql-server-2005 triggers

那么,

我正在创建一个检查备份例程的作业,并在它停止时发送和发送电子邮件(我希望这对其他人有用)。

这就是我正在做的事情:

1)我创建了一个查询,该查询返回在特定时间段内尚未备份的db_name和backup_type:

select name as "Nome da Base" , 'L' as Tipo from  sys.sysdatabases a 
where name not in ('master','tempdb','model'))
except
(select b.database_name, b.type from msdb..backupset b
where b.backup_start_date >= DATEADD (hour,-2, GETDATE())
and type='L')
union all
(select name as "Nome da Base", 'D' as Tipo from  sys.sysdatabases a 
where name not in ('tempdb','model'))
except
(select b.database_name, b.type from msdb..backupset b
where b.backup_start_date >= DATEADD (day,-1, GETDATE())
and type='D'

2)创建一个表来记录上面查询的返回行:

CREATE TABLE [dbo].[Alerta_Log_Bkp](
    [Nome da Base] [nvarchar](50) NULL,
    [Tipo] [nvarchar](8) NULL,
    [Servidor] [nvarchar](10) NULL,
    [Hora Verificação] [datetime] NULL
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Alerta_Log_Bkp] ADD  CONSTRAINT [DF_Alerta_Log_Bkp_Servidor]  
  DEFAULT (N'Server_NAME') FOR [Servidor]
GO

ALTER TABLE [dbo].[Alerta_Log_Bkp] ADD  CONSTRAINT [DF_Alerta_Log_Bkp_Último Backup]  
  DEFAULT (getdate()) FOR [Hora Verificação]
GO

3)使用“插入选择”创建作业:

INSERT INTO [dbo].[Alerta_Log_Bkp]
           ([Nome da Base],[Tipo])
(select name as "Nome da Base" , 'L' as Tipo from  sys.sysdatabases a 
where name not in ('master','tempdb','model','BA_CMA_OM'))
except
(select b.database_name, b.type from msdb..backupset b
where b.backup_start_date >= DATEADD (hour,-2, GETDATE())
and type='L')
union all
(select name as "Nome da Base", 'D' as Tipo from  sys.sysdatabases a 
where name not in ('tempdb','model'))
except
(select b.database_name, b.type from msdb..backupset b
where b.backup_start_date >= DATEADD (day,-1, GETDATE())
and type='D')
GO

- >好的,到目前为止一切都很好。

我的问题在于在“Alerta_Log_Bkp”表上创建一个触发器,当在其上插入一行时触发该电子邮件。如果我只用1行测试插件,一切正常。但是,如果插入行超过1行,我会收到以下错误: “子查询返回的值超过1。当子查询”

时,不允许这样做

我知道这种情况正在发生,因为我将变量设置为从插入中选择。但我无法想出另一条出路。

这是触发器:

Create TRIGGER [dbo].[TR_Alert_mail_BKP] ON [dbo].[Alerta_Log_Bkp] AFTER INSERT AS

DECLARE @base varchar(50)
DECLARE @tipo varchar(50)
DECLARE @servidor varchar(50)
DECLARE @last_bkp DATETIME


SET @base  = (SELECT "Nome da Base" FROM inserted)
SET @tipo = (SELECT tipo FROM inserted)
SET @servidor = (SELECT Servidor FROM inserted)

create table #temp (base varchar(50),
                    tipo varchar(50),
                    servidor varchar(50)) 
insert into #temp values (@base,@tipo,@servidor)

SET @last_bkp = (Select MAX(backup_start_date) from msdb..backupset
                Where database_name = (select base from #temp)
                            and type = (select tipo from #temp))

--SET @last_bkp = (Select MAX(backup_start_date) from msdb..backupset
--              Where database_name = @base and type = @tipo)

IF @tipo in ('D','L')
BEGIN
DECLARE @msg varchar(8000)
SET @msg = 'The "' +@tipo + '"  from database "' + @base + '", of the server "' 
    + @servidor + '", stoped at ' +  CAST(@last_bkp as varchar(50)) +''
EXEC msdb.dbo.sp_send_dbmail 
  @recipients=N'my_mail@domain.com', 
  @body= @msg, 
  @subject = 'Problemas no Backup'  , 
  @profile_name = 'PROFILE'
END

有没有人知道另一种编写触发器的方法,所以我不会得到子查询错误?

谢谢, FABRICIO

2 个答案:

答案 0 :(得分:1)

无论何时编写查询,将查询的值从插入或删除设置为标量变量,触发器都是错误的,需要重写。触发器对一组数据进行操作,而不是一次一行。所以你需要使用基于集合的逻辑。是否要为插入的每个记录或每个批次发送电子邮件?

此外,从触发器发送电子邮件不是一个好主意。最好将数据发送到另一个表,然后让作业间隔检查该表并发送电子邮件。

帮助您思考基于集合的转换

insert into #temp 
values (@base,@tipo,@servidor) 

insert into #temp 
Select  "Nome da Base", tipo,servidor from inserted

答案 1 :(得分:1)

这是我的建议。放下扳机。在Alerta_Log_Bkp表中添加一列:

ALTER TABLE dbo.Alerta_Log_Bkp ADD Alerted BIT NOT NULL DEFAULT 0;
UPDATE dbo.Alerta_Log_Bkp SET Alerted = 1; -- so that all past entries are marked as alerted

创建以下存储过程:

CREATE PROCEDURE dbo.AlertOnBackupHistoryRuleViolation
AS
BEGIN
    INSERT INTO [dbo].[Alerta_Log_Bkp]([Nome da Base],[Tipo])
    SELECT name, t = 'L' FROM sys.sysdatabases AS a 
      WHERE name NOT IN ('master','tempdb','model','BA_CMA_OM')
      EXCEPT SELECT b.database_name, b.type from msdb..backupset AS b
        WHERE b.backup_start_date >= DATEADD(HOUR, -2, CURRENT_TIMESTAMP) AND type = 'L'
    UNION ALL
    SELECT name, 'D' FROM sys.sysdatabases AS a 
      WHERE name NOT IN ('tempdb', 'model')
      EXCEPT SELECT b.database_name, b.type FROM msdb..backupset AS b
        WHERE b.backup_start_date >= DATEADD (day,-1, CURRENT_TIMESTAMP) AND type='D';

    IF @@ROWCOUNT > 0
    BEGIN
        DECLARE @body NVARCHAR(MAX); SET @body = N'';

        SELECT @body = @body + CHAR(13) + CHAR(10) 
            + 'The "' + a.Tipo + '"  from database "' + a.[Nome da Base] 
            + '", of the server "' + a.Servidor + '", stopped at ' 
            + COALESCE(CONVERT(VARCHAR(32), MAX(b.backup_start_date)), 'never')
        FROM dbo.Alerta_Log_Bkp AS a LEFT OUTER JOIN msdb.dbo.backupset AS b
        ON a.[Nome da Base] = b.database_name AND a.Tipo = b.[type] WHERE a.Alerted = 0
        GROUP BY a.Tipo, a.[Nome da Base], a.Servidor;

        EXEC msdb.dbo.sp_send_dbmail
             @recipients   = N'my_mail@domain.com', 
             @body         = @body, 
             @subject      = N'Problemas no Backup'  , 
             @profile_name = N'PROFILE';

        UPDATE dbo.Alerta_Log_Bkp SET Alerted = 1 WHERE Alerted = 0;
    END    
END
GO

现在从作业中调用存储过程。没有触发器,没有麻烦,没有大惊小怪。即使有40个数据库违反规则,也只需要一封电子邮件。