Подзапрос вернул более одного значения в триггере SQL Server 2005 [2] - PullRequest
0 голосов
/ 21 сентября 2011

Ну,

Я создаю задание, которое проверяет процедуры резервного копирования и отправляет и отправляет электронную почту всякий раз, когда это останавливается (надеюсь, это станет полезным для других).

Этокак я это делаю:

1) Я создал запрос, который возвращает db_name и тип backup_, который не был заархивирован в течение определенного периода:

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

У кого-нибудь есть подсказка о другом способе написания этого триггера, поэтому я не получу ошибку подзапроса?

Спасибо, Фабрицио

Ответы [ 2 ]

1 голос
/ 21 сентября 2011

Вот мое предложение.Отпустите курок.Добавьте столбец в таблицу 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 баз данных, которые нарушают это правило.

1 голос
/ 21 сентября 2011

Каждый раз, когда вы пишете запрос, устанавливая значение запроса из вставленного или удаленного в скалярную переменную, триггер неверен и его необходимо переписать.Триггеры работают с наборами данных не по одной строке за раз.Так что вам нужно использовать логику на основе множеств.Хотите отправить электронное письмо для каждой вставленной записи или каждой партии?

Кроме того, отправлять электронные письма из триггера не очень хорошая идея.Лучше отправлять данные в другую таблицу, а затем периодически проверять эту таблицу и отправлять электронные письма.

, чтобы помочь вам думать о множестве на основе преобразования этого

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

в

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