我有一个可以按照我想要的方式运行的SQL语句。
select COUNT(*), MIN(emailed_to)from email.email_archive
group by emailed_to
order by COUNT(*) desc
输出看起来像这样。
13 deadlockIE12388nnhy32@hepmeplease.com;
8 deadlockIE1277yhygt@hepmeplease.com;
4 deadlockFF17uyt9xx967@hepmeplease.com;
...
...
...
1 deadlockFF17uytsdfa7@hepmeplease.com;
这很简单,但是我必须记住每天运行选择并确保一切正常。我想让存储过程偶尔给我发电子邮件,我可以决定是否有问题。所以我从许多资源中攻击了以下内容:
use MYDB;
go
IF SCHEMA_ID('monitors') IS NULL EXECUTE('CREATE SCHEMA monitors AUTHORIZATION dbo')
GO
if object_id('monitors.email_abuse') is null
exec('create procedure monitors.email_abuse as print ''stub'' return');
GO
alter procedure monitors.email_abuse
(@to varchar(max) = 'itops@hepmeplease.com',
@sendemail tinyint = 1)
as
set nocount on ;
set transaction isolation level read uncommitted ;
begin try
declare @errmsg varchar(max) = '',
@subject nvarchar(255);
select @subject = 'Run Away Email Monitor';
select @errmsg = REPLICATE(char(10),1)+
'# of Emails'+
REPLICATE(char(9),1)+
'Email Address'+
REPLICATE(CHAR(10),1);
select @errmsg = @errmsg +REPLICATE(char(9),1)+
CAST(COUNT(*) as CHAR(10))+
REPLICATE(char(9),1)+
CAST(MIN(emailed_to) as CHAR(45))
from
email.email_archive
group by
emailed_to
order by
COUNT(*) desc;
print @errmsg;
if @sendemail = 1
begin
exec master.dbo.sp_email
@to = @to,
@subject = @subject,
@body = @errmsg;
end
end try
begin catch
-- unexpected errors
exec sp_raise_error @rethrow = 1, @textdata = N'Error in monitors.email_abuse', @emailTo = N'itops@hepmeplease.com'
end catch
go
但它然后通过电子邮件向我发送以下输出,这只是一行。我知道有很多行,但出于某些原因,当我将COUNT(*), MIN(emailed_to)
放入CAST
语句时,这不再起作用。我收到一封包含标题和一行的电子邮件。如果我只是打印@errmsg的输出我就是我在电子邮件中得到的,标题和一行。就像下面一样。
# of Emails Email Address
1 y@y.com;
我不确定我的演员声明我做错了什么。
答案 0 :(得分:2)
<强>解释强>
我的猜测是你实际使用的代码与你在这里发布的代码略有不同,因为当我把你的代码和以下数据放在测试数据库中时,事情就好了。
create table email_archive
(
id int,
emailed_to nvarchar(255)
)
insert into email_archive values
( 1, 'one@helpme.com'), ( 2, 'two@helpme.com'), ( 3, 'three@helpme.com'),
( 4, 'four@helpme.com'), ( 5, 'one@helpme.com'), ( 6, 'two@helpme.com'),
( 7, 'three@helpme.com'), ( 8, 'four@helpme.com'), ( 9, 'one@helpme.com'),
(10, 'two@helpme.com'), (11, 'three@helpme.com'), (12, 'four@helpme.com'),
(13, 'one@helpme.com'), (14, 'two@helpme.com'), (15, 'three@helpme.com'),
(16, 'four@helpme.com'), (17, 'one@helpme.com'), (18, 'one@helpme.com'),
(19, 'one@helpme.com'), (20, 'three@helpme.com'), (21, 'three@helpme.com')
我想你可能遇到了这里讨论的问题:http://bit.ly/cMlnjt
由于我无法确定我为您提供了两个可以完成工作的替代解决方案,即使正如其他人提到的那样这个聚合连接应该无需工作一个问题。
<强>备选方案:强>
为了得到你想要的东西,我更喜欢以下两个选项之一
1)只需让sp_send_dbmail为你完成工作。
2)使用光标解决方案
选项1:
EXEC msdb..sp_send_dbmail @profile_name = 'MyMailProfile',
@recipients = 'my_email@domain.com',
@subject = 'Runaway Email Monitor',
@body = 'Runaway emails found',
@query = 'SELECT COUNT(*), emailed_to FROM mydb.dbo.email_archive GROUP BY emailed_to HAVING COUNT(*) > 5 ORDER BY COUNT(*) DESC'
注意:having子句仅显示计数大于5的行。
选项2:
USE test
IF EXISTS ( SELECT name FROM test.sys.sysobjects WHERE type = 'P' AND name = 'usp_MonitorEmails' )
BEGIN
DROP PROCEDURE dbo.usp_MonitorEmails
END
GO
CREATE PROCEDURE usp_MonitorEmails
@Subject nvarchar(255) = '',
@Importance varchar(6) = 'NORMAL',
@Sensitivity varchar(12) = 'NORMAL',
@Recipients varchar(MAX) = NULL,
@MinimumCount int = 0
AS
BEGIN
SET NOCOUNT ON
IF UPPER(@Importance) NOT IN ('LOW', 'NORMAL', 'HIGH') SET @Importance = 'NORMAL'
IF UPPER(@Sensitivity) NOT IN ('NORMAL', 'PERSONAL', 'PRIVATE', 'CONFIDENTIAL') SET @Sensitivity = 'NORMAL'
DECLARE @run bit,
@message nvarchar(MAX)
SELECT @run = 0,
@subject = 'Run Away Email Monitor',
@message = 'Run away emails found' + CHAR(13)+CHAR(10) +
'Count Email Address' + CHAR(13)+CHAR(10) +
'----------- ------------------------------------------------------------------------------' + CHAR(13)+CHAR(10)
DECLARE @count int,
@email nvarchar(255)
DECLARE BodyCursor CURSOR STATIC FOR
SELECT COUNT(*), emailed_to FROM email_archive GROUP BY emailed_to HAVING COUNT(*) > @MinimumCount ORDER BY COUNT(*) DESC
OPEN BodyCursor
FETCH NEXT FROM BodyCursor
INTO @count, @email
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @message = @message + REPLICATE(N' ', 11-LEN(CAST(@count AS nvarchar(22)))) + CAST(@count AS nvarchar(22)) + ' ' + @email + CHAR(13)+CHAR(10), @run = 1
FETCH NEXT FROM BodyCursor
INTO @count, @email
END
CLOSE BodyCursor
DEALLOCATE BodyCursor
IF @run = 1 AND LEN(@Recipients) > 0
BEGIN
EXEC msdb..sp_send_dbmail @profile_name = 'MyMailProfile',
@recipients = @Recipients,
@subject = @Subject,
@body = @Message,
@body_format = 'TEXT',
@importance = @Importance,
@sensitivity = @Sensitivity
END
END
GO
注意:我更喜欢这种方法,因为我在格式化邮件方面具有灵活性。如果返回的行达到最小计数,这也将仅发送电子邮件。
答案 1 :(得分:0)
您只会获得一个值,因为您没有循环包含电子邮件的记录集。您需要使用CURSOR
遍历该集合,并使用每条记录的内容构建您的@errmsg
变量。
Nimble SQL jockeys可能会指出你可以避免CURSOR
在查询中使用正确的连接类型,但我总是觉得比这种方法更难,如果它不是高性能它会这份工作。
这是一些示例代码;
DECLARE @myGlobalVar nvarchar(255);
DECLARE @myVarCol1 nvarchar(10);
DECLARE @myVarCol2 nvarchar(10);
DECLARE myCursor CURSOR FOR
SELECT col1, col2
FROM table1
WHERE wanted = 1
ORDER BY col1;
OPEN myCursor;
FETCH NEXT FROM myCursor
INTO @myVarCol1, @myVarCol2;
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @myGlobalVar = @myGlobalVar + ' ' + @myVarCol1 + ' ' + @myVarCol2;
FETCH NEXT FROM myCursor
INTO @myVarCol1, @myVarCol2;
END
CLOSE vendor_cursor;
DEALLOCATE vendor_cursor;
永远记住循环中的FETCH NEXT
,除非您想要杀死进程!您应始终CLOSE
和DEALLOCATE
光标,否则最终会耗尽内存。
答案 2 :(得分:0)
在尝试使用此方法进行连接时,我遇到了类似的问题。通常一些搞乱SELECT的解决方案。 FOR XML连接方法更加健壮。
我试图在这里重现这个问题但是不能(它对我来说正常工作并在串联中输出2个不同的电子邮件地址)。
这对你有用吗?如果是这样,可以比较执行计划,看看有什么不同。
set nocount on
create table #email_archive
(
emailed_to CHAR(45)
)
insert into #email_archive
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE12388nnhy32@hepmeplease.com;' union all
select 'deadlockIE1277yhygt@hepmeplease.com;'
declare @errmsg varchar(max) = '',
@subject nvarchar(255) = 'Run Away Email Monitor';
select @errmsg = REPLICATE(char(10),1)+
'# of Emails'+
REPLICATE(char(9),1)+
'Email Address'+
REPLICATE(CHAR(10),1);
select @errmsg = @errmsg +REPLICATE(char(9),1)+
CAST(COUNT(*) as CHAR(10))+
REPLICATE(char(9),1)+
CAST(MIN(emailed_to) as CHAR(45))
from
#email_archive
group by
emailed_to
order by
COUNT(*) desc;
print @errmsg;
drop table #email_archive;
答案 3 :(得分:0)
上面的答案都是很好的答案..但最简单的是一夜之间来自Mike Talley。
select @errmsg = @errmsg +REPLICATE(char(9),1)+ CAST(COUNT(*) as CHAR(10))+ REPLICATE(CHAR(9),1)+ cast(substring(emailed_to, 1, 45) as char(45))+ REPLICATE(CHAR(10),1)
一旦我放下MIN并添加到子串中,监视器就像一个魅力......