sql >> Databáze >  >> Database Tools >> SSMS

Objekty SSMS SMO:Získejte výsledky dotazu

Nejjednodušší je vytisknout číslo, které dostanete zpět za ExecuteNonQuery :

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

To by mělo fungovat, ale nebude respektovat SET NOCOUNT nastavení aktuální relace/rozsahu.

Jinak byste to udělali jako s "obyčejným" ADO.NET. Nepoužívejte ServerConnection.ExecuteNonQuery() metodu, ale vytvořte SqlCommand objekt přístupem k základnímu SqlConnection objekt. Poté se přihlaste k odběru StatementCompleted událost.

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

Pomocí StatementCompleted (místo toho, řekněme, ručně vytiskněte hodnotu, která ExecuteNonQuery() return) má tu výhodu, že funguje přesně jako SSMS nebo SQLCMD.EXE:

  • Příkazy, které nemají ŘÁDEK, nebudou volány vůbec (např. GO, USE).
  • Pokud SET NOCOUNT ON byla nastavena, nebude volána vůbec.
  • Pokud SET NOCOUNT OFF byla nastavena, bude volána pro každý příkaz v dávce.

(Postranní panel:vypadá to jako StatementCompleted je přesně to, o čem protokol TDS mluví, když DONE_IN_PROC je zmíněna událost; viz Poznámky příkazu SET NOCOUNT na MSDN.)

Osobně jsem tento přístup s úspěchem použil ve svém vlastním "klonu" SQLCMD.EXE.

AKTUALIZACE :Je třeba poznamenat, že tento přístup (samozřejmě) vyžaduje ruční rozdělení vstupního skriptu/příkazů na GO oddělovač, protože jste se vrátili k používání SqlCommand.Execute*() který nedokáže zpracovat více dávek najednou. K tomu existuje několik možností:

  • Ručně rozdělte vstup na řádky začínající GO (upozornění:GO lze nazvat jako GO 5 , například pro provedení předchozí dávky 5krát).
  • Použijte ManagedBatchParser class/library, které vám pomohou rozdělit vstup do jednotlivých dávek, zejména implementovat ICommandExecutor.ProcessBatch s výše uvedeným kódem (nebo něčím podobným).

Vybral jsem si pozdější možnost, což byla docela práce, vzhledem k tomu, že to není dost dobře zdokumentované a příklady jsou vzácné (trochu googlujte, nějaké věci najdete, nebo použijte reflektor, abyste viděli, jak SMO-Assemblies tuto třídu používají) .

Výhoda (a možná i zátěž) používání ManagedBatchParser spočívá v tom, že bude také analyzovat všechny ostatní konstrukce skriptů T-SQL (určeno pro SQLCMD.EXE ) pro tebe. Včetně::setvar , :connect , :quit , atd. Nemusíte implementovat příslušný ICommandExecutor členů, pokud je vaše skripty samozřejmě nepoužívají. Ale mějte na paměti, že možná nebudete moci spouštět „libovolné“ skripty.

No, to tě položilo. Od „jednoduché otázky“, jak vytisknout „... dotčené řádky“ až po skutečnost, že to není triviální dělat robustním a obecným způsobem (vzhledem k požadované práci na pozadí). YMMV, hodně štěstí.

Aktualizace používání ManagedBatchParser

Zdá se, že neexistuje žádná dobrá dokumentace nebo příklad o tom, jak implementovat IBatchSource , zde je to, s čím jsem šel.

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 takto byste to použili:

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

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

parser.Parse();

Všimněte si, že obě implementace, buď pro přímé příkazy (StatementBatchSource ) nebo pro soubor (FileBatchSource ) mají problém, že přečtou celý text najednou do paměti. Měl jsem jeden případ, kdy to prasklo, protože jsem měl obrovský (!) skript s miliony generovaných INSERT prohlášení. I když si nemyslím, že to je praktický problém, SQLCMD.EXE mohl to zvládnout. Ale za celý svůj život jsem nemohl přijít na to, jak přesně, budete muset vytvořit bloky vrácené pro IBatchParser.GetContent() aby s nimi analyzátor mohl stále pracovat (vypadá to, že by to musely být úplné příkazy, což by v první řadě tak trochu mařilo účel analýzy...).




  1. Přístup odepřen při otevírání phpMyAdmin

  2. Jak načíst data ze serveru SQL na základě níže uvedeného příkladu?

  3. MySQL:Přístup odepřen pro uživatele 'userName'@'localhost'

  4. Chyba phpMyAdmin:#1054 - Neznámý sloupec 'systeem_eisen' v 'klauzuli objednávky'