如何获得像SSMS这样的单个行数?


8

我有一个客户端c#程序,正在通过执行存储过程ExectueNonQuery,包括PRINT使用InfoMessage事件捕获和错误输出。它工作正常,但我注意到有些奇怪。

当我从SSMS执行存储过程时,它将显示在“消息”选项卡中执行的每个单独的SQL语句的行数(就像它来自InfoMessages)。尽管我的程序确实捕获了所有其他相同的输出,但是我的程序从未看到这些消息。相反,它仅返回ExecuteNonQuery函数结果中受影响的行,该行是所有单个行计数之和(这是无用的)。

例如,此过程:

use [tempdb]
go

SELECT  * 
INTO    MyCols
FROM    sys.columns
go

CREATE PROC foo As

    UPDATE  MyCols
    SET     name = name + N''
-- SSMS shows (662 row(s) affected)

    UPDATE  MyCols
    SET     name = name + N''
    WHERE   name like '%x%'
-- SSMS shows (59 row(s) affected)

PRINT 'bar'
-- both SSMS and ExecuteNonQuery get this

-- ExecuteNonQuery returns 721 rows affected
GO

fooPROC运行的662和59,SSMS显示rowcounts,但ExecuteNonQuery只返回共有721处。

因此,如何获得与SSMS相同的信息?


这里要澄清一下:我对如何更改存储过程以PRINT @@ROWCOUNT在每个SQL语句后添加s 并不感兴趣。我知道该怎么做,由于种种原因,大多数时候这不是一个选择。

我在问SSMS在做什么。现在,我可以更改所有我想要的客户端代码(无论如何现在),我想正确地做。

Answers:


6

SqlCommand.StatementCompleted事件将在批处理中的每个语句之后触发,并且该事件的属性之一(几乎是唯一的属性)是受触发该事件的语句影响的行数。

一些注意事项:

  • 得到这个信息的要求是,你并没有指定SET NOCOUNT ON;,或者相反,你没有指定SET NOCOUNT OFF;
  • 所有事件均在每个事件完成时触发Execute___(),而不是在执行过程中触发。
  • StatementCompletedEventArgs.RecordCount包括从行数SELECT的语句,而SqlDataReader.RecordsAffected属性只从DML语句(报告行数INSERTUPDATEDELETE,等)。
  • StatementCompleted事件并没有包括从触发事件批次个别SQL语句。但是,事件处理程序是sender作为输入参数发送的,这是SqlCommand查询批处理的,您可以通过强制转换senderSqlCommand并查看CommandText属性来看到该批处理(如下面的示例所示)。

该文档是非常稀少的这个所以我已经做了一个例子来展示这个事件引发了双方ExecuteNonQueryExecuteScalar,以及两个特设的查询和存储过程(即SqlCommand.CommandTypeTextVS StoredProcedure):

using System;
using System.Data;
using System.Data.SqlClient;

namespace StatementCompletedFiring
{
    class Program
    {
        static void Main(string[] args)
        {
            using (SqlConnection _Connection =
                          new SqlConnection("Integrated Security = True;"))
            {
                using (SqlCommand _Command = new SqlCommand(@"
SET NOCOUNT OFF; --  ensures that the 'StatementCompleted' event fires

EXEC('
CREATE PROCEDURE #TestProc
AS
SELECT * FROM sys.objects;

SELECT * FROM sys.tables;
');

SELECT * FROM sys.objects;
", _Connection))
                {

                    _Command.StatementCompleted += _Command_StatementCompleted;

                    try
                    {
                        _Connection.Open();

                        _Command.ExecuteNonQuery();

                        _Command.CommandText = @"
SELECT 123 AS [Bob];

WAITFOR DELAY '00:00:05.000'; --5 second pause to shows when the events fire

SELECT 2 AS [Sally]
UNION ALL
SELECT 5;
";
                        Console.WriteLine("\n\t");
                        Console.WriteLine(_Command.ExecuteScalar().ToString());
                        Console.WriteLine("\n");


                        _Command.CommandType = CommandType.StoredProcedure;
                        _Command.CommandText = "#TestProc";
                        _Command.ExecuteNonQuery();
                    }
                    catch (Exception _Exception)
                    {
                        throw new Exception(_Exception.Message);
                    }
                }
            }
        }

        static void _Command_StatementCompleted(object sender,
                                                StatementCompletedEventArgs e)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("\nQuery Batch: ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(((SqlCommand)sender).CommandText);

            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("Row(s) affected: ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine(e.RecordCount.ToString() + "\n");

            Console.ResetColor();
        }
    }
}

输出:

查询批次:
SET NOCOUNT OFF; -确保触发“ StatementCompleted”事件

EXEC('CREATE PROCEDURE #TestProc AS SELECT * FROM sys.objects;

SELECT * FROM sys.tables; ');

SELECT * FROM sys.objects;

受影响的列: 453

查询批次:
SELECT 123 AS [Bob];

WAITFOR DELAY '00:00:05.000'; -5秒暂停

SELECT 2 AS [Sally] UNION ALL SELECT 5;

受影响的列: 1

查询批次:
SELECT 123 AS [Bob];

WAITFOR DELAY '00:00:05.000'; -5秒暂停

SELECT 2 AS [Sally] UNION ALL SELECT 5;

受影响的列: 2

123

查询批次: #TestProc
受影响的列: 453

查询批次: #TestProc
受影响的行: 17


1
我已经尝试过了,它确实对我有用。奇怪的是,存储过程中的PRINT语句中的StatementCompletions和InfoMessages似乎彼此不同步(我得到了一堆StatementCompletions,然后一堆PRINT语句输出,尽管它们应该是交错的)。猜猜这是另一天的SSMS技巧...
RBarryYoung

1
@RBarryYoung是的,即使令人讨厌,我相信这种行为也是可以预期的。它与TDS(表格数据流)中各项的顺序有关:msdn.microsoft.com/en-us/library/dd304523.aspx。我知道PRINTRAISERROR(..., 10, 1)消息都在结果集之后。我试图在该文档中找到消息顺序,但到目前为止还没有发现。
所罗门·鲁兹基

对我来说,神秘的是SSMS如何正确地对其进行分类。
RBarryYoung 2015年

1
@RBarryYoung也许这应该是一个单独的问题,因为这只是关于单个查询的行数?这是一个很好的问题,我已经弄清楚了:)。如果有机会,我将发布它作为一个问题。
所罗门·鲁兹基

1
@RBarryYoung Yikes。很抱歉得知。希望你会好起来的。我将在接下来的几天内尝试解决。发布最后一条消息后,我只剩下一两个变体可以测试一下。我将在此处发布链接。
所罗门·鲁兹基2015年

-1

executenonquery结果只是不会在这里做您想要的。但是您仍然可以到达那里,这仅取决于您要使用这些信息的目的。

您可以在每次插入“ PRINT @@ ROWCOUNT”之后添加此行,并且应该在输出中获得受先前操作影响的行数(在此处获得“条”)。

另外,您可以在存储过程中添加“ OUTPUT”参数以保存结果,然后在运行executenonquery时捕获该结果。

编辑:

我设法修改了Jonathan Kehasias放在一起的示例,以包括statementcomplete事件处理。只需添加这两行。

#Add handler for StatementCompleted
$statementhandler = {param($sender, [System.Data.StatementCompletedEventArgs]$event) Write-Host $event.RecordCount };

#Attach handler...
$cmd.add_StatementCompleted($statementhandler)

我无法修改这些步骤。我可以修改客户端代码,包括使用ExecuteNonQuery以外的其他代码。
RBarryYoung 2015年

您可以尝试将事件处理程序附加到sqlcommand infomessage事件。本文介绍了如何使用powershell进行操作。 sqlskills.com/blogs/jonathan / ...
Jonathan Fite

如果您阅读了我的问题,您会发现我已经在这样做了。它不在那儿。
RBarryYoung 2015年

1
C#区域中的这个问题表明,将侦听器添加到SQLCommand.StatementCompleted事件可以使他们找到所需的内容。 stackoverflow.com/questions/27993049/…–
乔纳森·菲特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.