Bagaimana saya bisa mendapatkan jumlah baris individual seperti SSMS?

8

Saya memiliki program c # klien yang menjalankan prosedur tersimpan melalui ExectueNonQuery, termasuk menangkap PRINToutput dan Kesalahan dengan peristiwa InfoMessage. Ini berfungsi dengan baik, tapi saya perhatikan ada yang aneh.

Ketika saya menjalankan prosedur tersimpan dari SSMS, ini menampilkan rowcounts untuk setiap pernyataan SQL individu yang dieksekusi di tab Pesan (seolah-olah itu berasal dari InfoMessages). Namun program saya tidak pernah melihat pesan-pesan ini, meskipun ia menangkap semua output lain yang sama. Sebaliknya, itu hanya mengembalikan baris yang terpengaruh dalam hasil fungsi ExecuteNonQuery yang merupakan jumlah dari semua jumlah baris individu (yang agak tidak berguna).

Sebagai contoh, prosedur ini:

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

Ketika fooproc dijalankan, SSMS menampilkan jumlah baris dari 662 dan 59, tetapi ExecuteNonQueryhanya mengembalikan total 721.

Jadi, bagaimana saya bisa mendapatkan informasi yang sama dengan SSMS?


Hanya untuk menjadi jelas di sini: Saya tidak tertarik dengan cara mengubah prosedur tersimpan untuk menambahkan PRINT @@ROWCOUNTsetelah setiap pernyataan SQL. Saya tahu bagaimana melakukan itu dan itu bukan pilihan sebagian besar waktu karena berbagai alasan.

Saya bertanya bagaimana melakukan apa yang dilakukan SSMS di sini. Saya dapat mengubah kode klien semua yang saya inginkan pada saat ini (untuk saat ini, toh) dan saya ingin melakukannya dengan benar.

RBarryYoung
sumber

Jawaban:

6

The SqlCommand.StatementCompletedacara akan api setelah setiap pernyataan dalam batch, dan salah satu sifat dari peristiwa (baik, cukup banyak satu-satunya properti) adalah jumlah baris yang dipengaruhi oleh pernyataan yang memicu event.

Beberapa catatan:

  • Persyaratan untuk mendapatkan info ini adalah bahwa Anda tidak menentukan SET NOCOUNT ON;, atau sebaliknya, Anda memang menentukan SET NOCOUNT OFF;.
  • Semua acara dipicu pada penyelesaian masing-masing Execute___(), bukan selama eksekusi.
  • The StatementCompletedEventArgs.RecordCounttermasuk jumlah baris dari SELECTlaporan, sedangkan SqlDataReader.RecordsAffected properti hanya melaporkan jumlah baris dari DML pernyataan ( INSERT, UPDATE, DELETE, dll).
  • The StatementCompletedacara tidak tidak termasuk pernyataan SQL individu dari batch yang memicu event. Namun, event handler dikirim sendersebagai parameter input dan ini adalah SqlCommandbatch permintaan, dan Anda bisa melihat batch itu dengan cara casting senderke SqlCommanddan kemudian melihat CommandTextproperti (ini ditunjukkan pada contoh di bawah).

Dokumentasi adalah sangat jarang pada ini jadi saya telah bekerja sampai contoh yang menunjukkan acara ini menembak untuk kedua ExecuteNonQuerydan ExecuteScalar, serta untuk kedua ad hoc query dan disimpan prosedur (yaitu SqlCommand.CommandTypedari Textvs 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();
        }
    }
}

KELUARAN:

Kueri Batch:
SET NOCOUNT MATI; - memastikan bahwa acara 'StatementCompleted' menyala

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

PILIH * DARI sys.tables; ');

PILIH * DARI sys.objects;

Baris yang terpengaruh: 453

Query Batch:
SELECT 123 AS [Bob];

WAITFOR DELAY '00: 00: 05.000 '; --5 jeda detik

PILIH 2 SEBAGAI [Sally] UNION ALL SELECT 5;

Baris yang terpengaruh: 1

Query Batch:
SELECT 123 AS [Bob];

WAITFOR DELAY '00: 00: 05.000 '; --5 jeda detik

PILIH 2 SEBAGAI [Sally] UNION ALL SELECT 5;

Baris yang terpengaruh: 2

123

Kueri Batch:
Baris #TestProc terpengaruh: 453

Query Batch: #TestProc
Row terpengaruh: 17

Solomon Rutzky
sumber
1
Saya sudah mencoba ini dan itu berhasil untuk saya. Anehnya, PernyataanCompletions dan InfoMessages dari pernyataan PRINT dalam prosedur yang tersimpan tampaknya tidak disinkronkan antara lain tebak itu trik SSMS untuk hari lain ...
RBarryYoung
1
@RBarryYoung Ya, perilaku itu, saya yakin sudah diharapkan, meskipun menjengkelkan. Ini berkaitan dengan urutan item dalam TDS (Tabular Data Stream): msdn.microsoft.com/en-us/library/dd304523.aspx . Saya tahu bahwa PRINTdan RAISERROR(..., 10, 1)pesan semua datang setelah set hasil. Saya mencoba menemukan pesan yang dipesan dalam dokumentasi itu, tetapi sejauh ini belum menemukannya.
Solomon Rutzky
Misteri bagi saya adalah bagaimana SSMS mengatasinya dengan benar.
RBarryYoung
1
@RBarryYoung Mungkin ini harus menjadi pertanyaan yang terpisah karena ini hanya tentang jumlah baris dari permintaan individu? Ini pertanyaan yang bagus, dan saya sudah mengetahuinya :). Saya akan mempostingnya sebagai pertanyaan jika saya mendapatkan kesempatan untuk melakukannya sebelum Anda mendapatkannya.
Solomon Rutzky
1
@RBarryYoung Yikes. Maaf mendengar tentang itu. Semoga Anda dalam perbaikan. Saya akan mencoba untuk mendapatkannya dalam beberapa hari ke depan. Saya hanya memiliki satu atau dua variasi tersisa untuk menguji yang saya pikirkan setelah saya memposting pesan terakhir itu. Saya akan memposting tautannya di sini.
Solomon Rutzky
-1

Hasil eksekusi permintaan tidak akan melakukan apa yang Anda inginkan di sini. Tetapi Anda masih bisa sampai di sana, itu hanya tergantung pada apa Anda ingin menggunakan informasi.

Anda dapat menambahkan baris ini setelah setiap sisipan "PRINT @@ ROWCOUNT" dan Anda harus mendapatkan jumlah baris yang dipengaruhi oleh operasi sebelumnya sebagai bagian dari output (di mana Anda mendapatkan "bar".

Atau, Anda dapat menambahkan parameter "OUTPUT" ke prosedur tersimpan Anda untuk menyimpan hasil dan kemudian hanya menangkap itu ketika Anda menjalankan file eksekusi.

EDIT:

Saya berhasil memodifikasi sampel yang Jonathan Kehasias kumpulkan untuk menyertakan penanganan acara yang dilengkapi pernyataan. Tambahkan saja dua baris ini.

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

#Attach handler...
$cmd.add_StatementCompleted($statementhandler)
Jonathan Fite
sumber
Saya tidak dapat mengubah prosedur ini. Saya dapat memodifikasi kode klien, termasuk menggunakan sesuatu selain ExecuteNonQuery.
RBarryYoung
Anda bisa mencoba melampirkan pengendali acara ke acara infomessage sqlcommand. Artikel ini menunjukkan cara melakukannya menggunakan PowerShell. sqlskills.com/blogs/jonathan/…
Jonathan Fite
Jika Anda membaca pertanyaan saya, Anda akan melihat bahwa saya sudah melakukan itu. Itu tidak ada di sana.
RBarryYoung
1
Pertanyaan ini di area C # mengatakan bahwa menambahkan pendengar ke acara SQLCommand.StatementCompleted memberi mereka apa yang mereka cari. stackoverflow.com/questions/27993049/…
Jonathan Fite