2011-09-20 57 views
0

好,子查詢返回的SQL Server超過1個值2005觸發[2]

我創建檢查備份程序,每當它停止發送和郵件工作(我希望這將成爲有用給其他人)。

這就是我正在做的方式:

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 "' [email protected] + '" from database "' + @base + '", of the server "' 
    + @servidor + '", stoped at ' + CAST(@last_bkp as varchar(50)) +'' 
EXEC msdb.dbo.sp_send_dbmail 
    @recipients=N'[email protected]', 
    @body= @msg, 
    @subject = 'Problemas no Backup' , 
    @profile_name = 'PROFILE' 
END 

沒有人有另一種方式來寫觸發線索,所以我不會得到子查詢的錯誤?

感謝, 法布里西奧

+0

根據@HLGEM的建議,請參閱以下文章,瞭解爲什麼要避免從觸發器發送電子郵件 - 即使sp_send_dbmail類似地是異步的:http://sqlblog.com/blogs/aaron%5Fbertrand/ archive/2009/10/12/bad-habits-to-kick-abusing-triggers.aspx –

回答

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'[email protected]', 
      @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個違反規則的數據庫,也只需一個電子郵件。

+0

亞倫非常感謝你。爲了創建存儲過程,我必須將'SELECT @body + = CHAR(13)+ CHAR(10)'更改爲'SELECT @body = CHAR(13)+ CHAR(10)'(是嗎?) 。另外,我刪除了「;」從這一行:'+ COALESCE(CONVERT(VARCHAR(32),MAX(b.backup_start_date)),'從不');'。然後,執行SP,但導致相同的錯誤:子查詢錯誤。 –

+0

對不起,我以爲你是在2008年。它需要是'SELECT @body = @body + CHAR(13)+ ...'更正。 –

+0

亞倫,它像一個魅力。非常感謝您的幫助! –

1

任何時候當你編寫一個查詢中設置查詢的值從插入或刪除一個標量變量,觸發是錯誤的,需要重寫。觸發器一次對一組數據操作,而不是一行。所以你需要使用基於集合的邏輯。您是否想要爲插入的每個記錄或每個批次發送一封電子郵件?

此外,從觸發器發送電子郵件並不是一個好主意。最好將數據發送到另一個表格,然後讓作業每隔一段時間檢查一次表格併發送電子郵件。

,以幫助您思考基於集的轉換這

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

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

亞倫,感謝您的快速回復。 我讀過你的博客,是的,我來自甲骨文。實際上,在編寫T-SQL時我沒有任何期望。 您是否看到用「select * from inserted」的結果發送電子郵件的方法,從msdb添加MAX(backup_start_date)列? 再次感謝, Fabricio –

+1

爲什麼觸發器必須這樣做?如果一項工作正在插入數據,該作業是否也能發送電子郵件? –

+0

這很有道理。你有一個基本的例子來說明如何在電子郵件中發送選擇結果嗎? –

相關問題