存储过程需要统计N年来的所有数据,耗时长久。我结合SqlConnection的InfoMessage事件捕获进度,却不能实时捕获!请问是什么原因?
存储过程部份代码如下:
-- 存储过程参数:@p_StartRQ:起始日期,@p_EndRQ:终止日期
DECLARE @intDIFF int, @intAddValue int, @dtNewStartRQ datetime
SET @intAddValue = 0 -- 递增变量
SET @intDIFF = DATEDIFF(day,@p_StartRQ,@p_EndRQ)+1 -- 两日期天数之差,即总循环次数
SET @dtNewStartRQ = @p_StartRQ
WHILE @dtNewStartRQ <= @p_EndRQ
BEGIN
-- 执行N行统计代码......
SET @intAddValue = @intAddValue + 1 -- 变量递增1
-- 向前台传送当前进度消息
PRINT CONVERT(VARCHAR(20),@intAddValue)+'/'+CONVERT(VARCHAR(20),@intDIFF)
-- 日期递增1天,准备统计下一天
SET @dtNewStartRQ = DATEADD(day,@intAddValue,@p_StartRQ)
ENDC#前台SqlConnection的InfoMessage事件代码如下:
private void btnRun_Click(object sender, EventArgs e)
{
using (SqlConnection sqlConn = SqlConnInfo.CreateSqlConn())
{
sqlConn.InfoMessage += new SqlInfoMessageEventHandler(sqlConn_InfoMessage);
using (SqlCommand sqlCmd = sqlConn.CreateCommand())
{
try
{
sqlCmd.CommandText = "sp_MySpName";
sqlCmd.CommandType = CommandType.StoredProcedure;
// 参数赋值,省略......
sqlConn.Open();
sqlCmd.ExecuteNonQuery();
}
finally
{
sqlCmd.Dispose();
sqlConn.Close();
sqlConn.Dispose();
}
}
}
}
void sqlConn_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
//foreach (SqlError err in e.Errors)
//{
// MessageBox.Show(string.Format(
// "The “{0}” has received a severity “{1}”, state “{2}” error number “{3}”\n" +
// "on line “{4}” of procedure “{5}” on server “{6}”:\n“{7}”",
// err.Source, err.Class, err.State, err.Number, err.LineNumber,
// err.Procedure, err.Server, err.Message));
//}
MessageBox.Show(e.Message);
}
从MessageBox对话框看出InfoMessage事件只被触发了一次,存储过程所有PRINT内容被累加在一起返回?这样就不能实现实时显示存储过程执行进度情况了!不知是否我有什么地方没有设置?请大家指教。给分。
存储过程部份代码如下:
-- 存储过程参数:@p_StartRQ:起始日期,@p_EndRQ:终止日期
DECLARE @intDIFF int, @intAddValue int, @dtNewStartRQ datetime
SET @intAddValue = 0 -- 递增变量
SET @intDIFF = DATEDIFF(day,@p_StartRQ,@p_EndRQ)+1 -- 两日期天数之差,即总循环次数
SET @dtNewStartRQ = @p_StartRQ
WHILE @dtNewStartRQ <= @p_EndRQ
BEGIN
-- 执行N行统计代码......
SET @intAddValue = @intAddValue + 1 -- 变量递增1
-- 向前台传送当前进度消息
PRINT CONVERT(VARCHAR(20),@intAddValue)+'/'+CONVERT(VARCHAR(20),@intDIFF)
-- 日期递增1天,准备统计下一天
SET @dtNewStartRQ = DATEADD(day,@intAddValue,@p_StartRQ)
ENDC#前台SqlConnection的InfoMessage事件代码如下:
private void btnRun_Click(object sender, EventArgs e)
{
using (SqlConnection sqlConn = SqlConnInfo.CreateSqlConn())
{
sqlConn.InfoMessage += new SqlInfoMessageEventHandler(sqlConn_InfoMessage);
using (SqlCommand sqlCmd = sqlConn.CreateCommand())
{
try
{
sqlCmd.CommandText = "sp_MySpName";
sqlCmd.CommandType = CommandType.StoredProcedure;
// 参数赋值,省略......
sqlConn.Open();
sqlCmd.ExecuteNonQuery();
}
finally
{
sqlCmd.Dispose();
sqlConn.Close();
sqlConn.Dispose();
}
}
}
}
void sqlConn_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
//foreach (SqlError err in e.Errors)
//{
// MessageBox.Show(string.Format(
// "The “{0}” has received a severity “{1}”, state “{2}” error number “{3}”\n" +
// "on line “{4}” of procedure “{5}” on server “{6}”:\n“{7}”",
// err.Source, err.Class, err.State, err.Number, err.LineNumber,
// err.Procedure, err.Server, err.Message));
//}
MessageBox.Show(e.Message);
}
从MessageBox对话框看出InfoMessage事件只被触发了一次,存储过程所有PRINT内容被累加在一起返回?这样就不能实现实时显示存储过程执行进度情况了!不知是否我有什么地方没有设置?请大家指教。给分。
DECLARE @intDIFF int, @intAddValue int, @dtNewStartRQ datetime
SET @intAddValue = 0 -- 递增变量
SET @intDIFF = DATEDIFF(day,@p_StartRQ,@p_EndRQ)+1 -- 两日期天数之差,即总循环次数
SET @dtNewStartRQ = @p_StartRQ
WHILE @dtNewStartRQ <= @p_EndRQ
BEGIN
-- 执行N行统计代码......
SET @intAddValue = @intAddValue + 1 -- 变量递增1
-- 向前台传送当前进度消息
raiserror( CONVERT(VARCHAR(20),@intAddValue)+'/'+CONVERT(VARCHAR(20),@intDIFF),16,1)
-- 日期递增1天,准备统计下一天
SET @dtNewStartRQ = DATEADD(day,@intAddValue,@p_StartRQ)
END
raiserror('产品不存在',16,1)
begin
return -1
end
在前台获取返回参数,然后rensponse.write
一样不行,我是从raiserror改为print的。
如果同时有5个人在统计,服务器会受不了的吧,不过看你这个需求,估计也就月结的时候一个人在操作,问题也不大,我只是纯技术角度讨论这个问题
我记得李维的csdn演讲录音里在讨论项目的运算是放在前端还是后端时说过一句话:对于统计这种项目,运算应该放在客户端,因为运算不是数据库的强项,他做过一个实验,一个项目决定听取那些oracle的人的建议,统计运算都放在数据库端,几个人一起上线,数据库卡死在那里跑不动了
根据楼主的情况,建议楼主能够把返回的MSG类型变为表类型,可能会好些。
每执行一步COMMIT一下。
在外面访问该表。
有人了解SqlConnection的InfoMessage事件原理吗?是不是真的只会触发一次?
跟你的区别在于设置了con.FireInfoMessageEventOnUserErrors = true;
如果不设置FireInfoMessageEventOnUserErrors,抛出的错误给SqlConnection捕获了.存储过程create procedure sp_InfoMsgTest
@times int
as
begin
declare @msg NVARCHAR(100)
while(@times>0)
begin
set @msg = 'Error' + cast(@times as VARCHAR(5))
raiserror(@msg,16,1)
set @times =@times - 1
end
end
C# WinForm前台代码 private const string conString = "Server=SZSOFT-SZZB\\SQLEXPRESS;database=office;uid=sa;pwd=abc_!123456;";
private void button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection();
con.ConnectionString = conString;
con.FireInfoMessageEventOnUserErrors = true;
con.InfoMessage += new SqlInfoMessageEventHandler(con_InfoMessage);
con.Open(); SqlCommand cmd = new SqlCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "sp_InfoMsgTest";
cmd.Connection = con;
cmd.Parameters.Add(new SqlParameter("@times", 10));
cmd.ExecuteNonQuery();
} void con_InfoMessage(object sender, SqlInfoMessageEventArgs e)
{
Console.WriteLine(e.Message);
}
非常感谢iStarSoft的详解,我已测试过了,确实可行。再次感谢!加100分。
con_InfoMessage被多次调用是在存储过程执行完之後才多次调用的
在执行过程中是绝对不会调用你的con_InfoMessage。
你试这个存储过程
create procedure sp_InfoMsgTest
@times int
as
begin
declare @msg NVARCHAR(100)
while(@times>0)
begin
set @msg = 'Error' + cast(@times as VARCHAR(5))
raiserror(@msg,16,1)
set @times =@times - 1
WAITFOR DELAY '00:00:10' --模拟一个长时间的执行过程
end
end
Garnett_KG说得没错,确实是存储过程执行完之後才多次调用的。
我开始只是简单测试了一下,发现是被多次调用了InfoMessage事件。后来我就用延时函数进行详细测试,才发现在存储过程执行时并没有及时触发InfoMessage事件,只是在执行完之后才被多次调用。被微软忽悠了。