SSMS
 sql >> Cơ Sở Dữ Liệu >  >> Database Tools >> SSMS

Đối tượng SSMS SMO:Nhận kết quả truy vấn

Điều dễ dàng nhất có thể là chỉ cần in số bạn nhận lại cho ExecuteNonQuery :

int rowsAffected = server.ConnectionContext.ExecuteNonQuery(/* ... */);
if (rowsAffected != -1)
{
     Console.WriteLine("{0} rows affected.", rowsAffected);
}

Điều này sẽ hoạt động, nhưng sẽ không tôn trọng SET NOCOUNT cài đặt của phiên / phạm vi hiện tại.

Nếu không, bạn sẽ làm như cách bạn làm với ADO.NET "đơn giản". Không sử dụng ServerConnection.ExecuteNonQuery() nhưng hãy tạo một SqlCommand đối tượng bằng cách truy cập SqlConnection bên dưới vật. Trên đó, đăng ký StatementCompleted sự kiện.

using (SqlCommand command = server.ConnectionContext.SqlConnectionObject.CreateCommand())
{
    // Set other properties for "command", like StatementText, etc.

    command.StatementCompleted += (s, e) => {
         Console.WriteLine("{0} row(s) affected.", e.RecordCount);
    };

    command.ExecuteNonQuery();
}

Sử dụng StatementCompleted (thay vào đó, giả sử in thủ công giá trị ExecuteNonQuery() trả về) có lợi ích là nó hoạt động chính xác như SSMS hoặc SQLCMD.EXE sẽ:

  • Đối với các lệnh không có ROWCOUNT, lệnh đó sẽ hoàn toàn không được gọi (ví dụ:ĐI, SỬ DỤNG).
  • Nếu SET NOCOUNT ON đã được thiết lập, nó sẽ hoàn toàn không được gọi.
  • Nếu SET NOCOUNT OFF đã được thiết lập, nó sẽ được gọi cho mọi câu lệnh trong một lô.

(Thanh bên:có vẻ như StatementCompleted chính xác là những gì giao thức TDS nói về khi DONE_IN_PROC sự kiện được đề cập; xem Nhận xét của lệnh SET NOCOUNT trên MSDN.)

Cá nhân tôi đã sử dụng phương pháp này thành công trong "bản sao" SQLCMD.EXE của riêng tôi.

CẬP NHẬT :Cần lưu ý rằng cách tiếp cận này (tất nhiên) yêu cầu bạn phải tách thủ công tập lệnh / câu lệnh đầu vào tại GO dấu phân tách, vì bạn đang quay lại sử dụng SqlCommand.Execute*() mà không thể xử lý nhiều lô cùng một lúc. Đối với điều này, có nhiều lựa chọn:

  • Tách thủ công đầu vào trên các dòng bắt đầu bằng GO (báo trước:GO có thể được gọi là GO 5 , ví dụ:để thực hiện lô trước đó 5 lần).
  • Sử dụng ManagedBatchParser class / library để giúp bạn chia dữ liệu đầu vào thành các lô duy nhất, đặc biệt là triển khai ICommandExecutor.ProcessBatch với đoạn mã ở trên (hoặc cái gì đó tương tự như nó).

Tôi chọn tùy chọn sau, cách này khá hiệu quả, vì nó không được ghi chép đầy đủ và rất hiếm ví dụ (hãy google một chút, bạn sẽ tìm thấy một số thứ hoặc sử dụng phản xạ để xem cách SMO-Assemblies sử dụng lớp đó) .

Lợi ích (và có thể là gánh nặng) của việc sử dụng ManagedBatchParser nghĩa là nó cũng sẽ phân tích cú pháp tất cả các cấu trúc khác của tập lệnh T-SQL (dành cho SQLCMD.EXE ) cho bạn. Bao gồm::setvar , :connect , :quit , v.v. Bạn không phải triển khai ICommandExecutor tương ứng tất nhiên, nếu các tập lệnh của bạn không sử dụng chúng. Nhưng xin lưu ý với bạn rằng bạn sẽ không thể thực thi các tập lệnh "tùy ý".

Vâng, đã làm điều đó đặt bạn. Từ "câu hỏi đơn giản" về cách in "... hàng bị ảnh hưởng" đến thực tế là không hề nhỏ để thực hiện một cách mạnh mẽ và chung chung (với yêu cầu công việc nền). YMMV, chúc may mắn.

Cập nhật về Sử dụng ManagedBatchParser

Dường như không có tài liệu hoặc ví dụ nào tốt về cách triển khai IBatchSource , đây là những gì tôi đã đi cùng.

internal abstract class BatchSource : IBatchSource
{
    private string m_content;

    public void Populate()
    {
        m_content = GetContent();
    }

    public void Reset()
    {
        m_content = null;
    }

    protected abstract string GetContent();

    public ParserAction GetMoreData(ref string str)
    {
        str = null;

        if (m_content != null)
        {
            str = m_content;
            m_content = null;
        }

        return ParserAction.Continue;
    }
}

internal class FileBatchSource : BatchSource
{
    private readonly string m_fileName;

    public FileBatchSource(string fileName)
    {
        m_fileName = fileName;
    }

    protected override string GetContent()
    {
        return File.ReadAllText(m_fileName);
    }
}

internal class StatementBatchSource : BatchSource
{
    private readonly string m_statement;

    public StatementBatchSource(string statement)
    {
        m_statement = statement;
    }

    protected override string GetContent()
    {
        return m_statement;
    }
}

Và đây là cách bạn sẽ sử dụng nó:

var source = new StatementBatchSource("SELECT GETUTCDATE()");
source.Populate();

var parser = new Parser(); 
parser.SetBatchSource(source);
/* other parser.Set*() calls */

parser.Parse();

Lưu ý rằng cả hai cách triển khai, hoặc cho các câu lệnh trực tiếp (StatementBatchSource ) hoặc cho một tệp (FileBatchSource ) có vấn đề là họ đọc toàn bộ văn bản cùng lúc trong bộ nhớ. Tôi đã gặp một trường hợp mà điều đó đã nổ tung, có một tập lệnh (!) Khổng lồ với các gazillions của INSERT được tạo các câu lệnh. Mặc dù tôi không nghĩ đó là một vấn đề thực tế, SQLCMD.EXE có thể xử lý nó. Nhưng đối với cuộc đời của tôi, tôi không thể tìm ra cách chính xác, bạn sẽ cần phải tạo các khối được trả về cho IBatchParser.GetContent() để theparser vẫn có thể làm việc với chúng (có vẻ như chúng cần phải là những câu lệnh hoàn chỉnh, điều này sẽ đánh bại mục đích của phân tích cú pháp ngay từ đầu ...).




  1. DBeaver
  2.   
  3. phpMyAdmin
  4.   
  5. Navicat
  6.   
  7. SSMS
  8.   
  9. MySQL Workbench
  10.   
  11. SQLyog
  1. Làm cách nào để cấp cho mình quyền truy cập quản trị vào phiên bản SQL Server cục bộ?

  2. Cách tìm tên máy chủ của SQL Server Management Studio

  3. Phạm vi ngày cho tập hợp dữ liệu giống nhau

  4. Cách hiển thị các giá trị trong Col3 trong đó các giá trị Col1 là đúng và các giá trị sai trong Col1 chỉ hiển thị NULL trong Col3

  5. SQL nhóm các số hoạt động và nhận tổng phụ và tổng cộng