SSMS
 sql >> Baza danych >  >> Database Tools >> SSMS

SSMS SMO Objects:Uzyskaj wyniki zapytania

Najłatwiej jest po prostu wydrukować numer, który otrzymasz z powrotem dla ExecuteNonQuery :

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

To powinno działać, ale nie będzie honorować SET NOCOUNT ustawienie bieżącej sesji/zakresu.

W przeciwnym razie zrobiłbyś to tak, jak robiłbyś z „zwykłym” ADO.NET. Nie używaj ServerConnection.ExecuteNonQuery() metody, ale utwórz SqlCommand obiekt, uzyskując dostęp do bazowego SqlConnection obiekt. W tym celu zasubskrybuj StatementCompleted wydarzenie.

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();
}

Korzystanie z StatementCompleted (zamiast tego, powiedzmy, ręcznie wypisz wartość, którą ExecuteNonQuery() zwracane) ma tę zaletę, że działa dokładnie tak, jak SSMS lub SQLCMD.EXE:

  • W przypadku poleceń, które nie mają ROWCOUNT, nie zostanie ono w ogóle wywołane (np. GO, USE).
  • Jeśli SET NOCOUNT ON został ustawiony, w ogóle nie zostanie wywołany.
  • Jeżeli SET NOCOUNT OFF została ustawiona, będzie wywoływana dla każdej instrukcji w paczce.

(Pasek boczny:wygląda jak StatementCompleted jest dokładnie tym, o czym mówi protokół TDS, gdy DONE_IN_PROC wydarzenie jest wymienione; zobacz Uwagi polecenia SET NOCOUNT w witrynie MSDN.)

Osobiście z powodzeniem zastosowałem to podejście w moim własnym „klonie” SQLCMD.EXE.

AKTUALIZUJ :Należy zauważyć, że to podejście (oczywiście) wymaga ręcznego podzielenia skryptu wejściowego/instrukcji w GO separatora, ponieważ wróciłeś do używania SqlCommand.Execute*() które nie mogą obsługiwać wielu partii jednocześnie. W tym celu istnieje wiele opcji:

  • Ręcznie podziel dane wejściowe na linie zaczynające się od GO (zastrzeżenie:GO można nazwać jak GO 5 , na przykład, aby wykonać poprzednią partię 5 razy).
  • Użyj ManagedBatchParser class/library, które pomogą Ci podzielić dane wejściowe na pojedyncze partie, w szczególności zaimplementuj ICommandExecutor.ProcessBatch z powyższym kodem (lub czymś przypominającym go).

Wybrałem późniejszą opcję, która była dość pracochłonna, biorąc pod uwagę, że nie jest dobrze udokumentowana, a przykłady są rzadkie (wygoogluj trochę, znajdziesz trochę rzeczy lub użyj reflektora, aby zobaczyć, jak SMO-Assembly używają tej klasy) .

Korzyść (i być może obciążenie) z używania ManagedBatchParser jest to, że przeanalizuje również wszystkie inne konstrukcje skryptów T-SQL (przeznaczonych dla SQLCMD.EXE ) dla Was. W tym::setvar , :connect , :quit itp. Nie musisz implementować odpowiedniego ICommandExecutor członków, jeśli twoje skrypty ich nie używają, oczywiście. Pamiętaj jednak, że możesz nie być w stanie wykonać „dowolnych” skryptów.

Cóż, czy to cię postawiło. Od "prostego pytania" jak wydrukować "... wiersze dotknięte" do faktu, że nie jest to trywialne wykonanie w solidny i ogólny sposób (biorąc pod uwagę wymaganą pracę w tle). YMMV, powodzenia.

Aktualizacja dotycząca użycia ManagedBatchParser

Wydaje się, że nie ma dobrej dokumentacji ani przykładu na temat implementacji IBatchSource , oto, z czym poszedłem.

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;
    }
}

A tak byś tego używał:

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

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

parser.Parse();

Zauważ, że obie implementacje, zarówno dla instrukcji bezpośrednich (StatementBatchSource ) lub dla pliku (FileBatchSource ) mają problem, że od razu wczytują cały tekst do pamięci. Miałem jeden przypadek, w którym to wybuchło, mając ogromny (!) skrypt z miliardami wygenerowanych INSERT sprawozdania. Chociaż nie sądzę, że jest to praktyczny problem, SQLCMD.EXE może sobie z tym poradzić. Ale na całe życie nie mogłem zrozumieć, jak dokładnie trzeba by utworzyć fragmenty zwrócone dla IBatchParser.GetContent() aby parser mógł nadal z nimi pracować (wygląda na to, że musiałyby to być kompletne instrukcje, które w pewnym sensie pokonałyby cel parsowania w pierwszej kolejności...).




  1. DBeaver
  2.   
  3. phpMyAdmin
  4.   
  5. Navicat
  6.   
  7. SSMS
  8.   
  9. MySQL Workbench
  10.   
  11. SQLyog
  1. Wyświetlanie liczby wierszy z serwera SQL do programu

  2. SSMS — wersja zapoznawcza SQL Azure v12 — nieznana właściwość IsNativelyCompiled

  3. Automatycznie odświeżać zapytanie w studiu zarządzania serwerem ms sql?

  4. Jak przeglądać dane w zmiennych tabeli podczas sesji debugowania w MS SQL Management Studio 2012?

  5. IntelliSense nie działa w programie SQL Server Management Studio