sql >> Databasteknik >  >> Database Tools >> SSMS

SSMS SMO-objekt:Få sökresultat

Det enklaste är möjligen att bara skriva ut numret du får tillbaka för ExecuteNonQuery :

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

Detta borde fungera, men kommer inte att respektera SET NOCOUNT inställning av aktuell session/omfattning.

Annars skulle du göra det som du skulle göra med "vanligt" ADO.NET. Använd inte ServerConnection.ExecuteNonQuery() metod, men skapa en SqlCommand objekt genom att komma åt den underliggande SqlConnection objekt. På det prenumererar du på StatementCompleted händelse.

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

Använder StatementCompleted (istället säg att manuellt skriva ut värdet som ExecuteNonQuery() returnerad) har fördelen att den fungerar precis som SSMS eller SQLCMD.EXE skulle:

  • För kommandon som inte har ett ROWCOUNT kommer det inte att anropas alls (t.ex. GO, USE).
  • Om SET NOCOUNT ON var inställd kommer den inte att anropas alls.
  • Om SET NOCOUNT OFF var inställd kommer den att anropas för varje sats i en batch.

(Sidofält:det ser ut som StatementCompleted är precis vad TDS-protokollet talar om när DONE_IN_PROC händelse nämns; se Anmärkningar av kommandot SET NOCOUNT på MSDN.)

Personligen har jag använt denna metod med framgång i min egen "klon" av SQLCMD.EXE.

UPPDATERA :Det bör noteras att detta tillvägagångssätt (naturligtvis) kräver att du manuellt delar upp inmatningsskriptet/påståendena vid GO separator, eftersom du har börjat använda SqlCommand.Execute*() som inte kan hantera flera batcher samtidigt. För detta finns det flera alternativ:

  • Dela in inmatningen manuellt på rader som börjar med GO (varning:GO kan kallas som GO 5 , till exempel för att köra föregående batch 5 gånger).
  • Använd ManagedBatchParser klass/bibliotek för att hjälpa dig dela upp inmatningen i enstaka partier, implementera särskilt ICommandExecutor.ProcessBatch med koden ovan (eller något som liknar den).

Jag väljer det senare alternativet, vilket var ganska jobbigt, med tanke på att det inte är särskilt väldokumenterat och exempel är sällsynta (googla lite, du hittar lite grejer, eller använd reflektor för att se hur SMO-församlingarna använder den klassen) .

Fördelen (och kanske bördan) med att använda ManagedBatchParser är att den också kommer att analysera alla andra konstruktioner av T-SQL-skript (avsedda för SQLCMD.EXE ) till dig. Inklusive::setvar , :connect , :quit , etc. Du behöver inte implementera respektive ICommandExecutor medlemmar, om dina skript inte använder dem, naturligtvis. Men kom ihåg att du kanske inte kommer att kunna köra "godtyckliga" skript.

Tja, var gjorde det dig. Från den "enkla frågan" om hur man skriver ut "...rader påverkade" till att det inte är trivialt att göra på ett robust och generellt sätt (med tanke på det bakgrundsarbete som krävs). YMMV, lycka till.

Uppdatering om ManagedBatchParser-användning

Det verkar inte finnas någon bra dokumentation eller exempel på hur man implementerar IBatchSource , här är vad jag gick med.

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

Och så här skulle du använda det:

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

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

parser.Parse();

Observera att båda implementeringarna, antingen för direkta uttalanden (StatementBatchSource ) eller för en fil (FileBatchSource ) har problemet att de läser hela texten på en gång i minnet. Jag hade ett fall där det exploderade, med ett enormt(!) skript med mängder av genererade INSERT uttalanden. Även om jag inte tror att det är en praktisk fråga, SQLCMD.EXE kunde hantera det. Men för mitt liv kunde jag inte ta reda på exakt hur du skulle behöva bilda de bitar som returneras för IBatchParser.GetContent() så att parsern fortfarande kan arbeta med dem (det ser ut som att de skulle behöva vara kompletta påståenden, vilket på ett sätt skulle motverka syftet med analysen i första hand...).




  1. Lägger till begränsningar i phpMyAdmin

  2. PhpMyAdmin installationsfel

  3. Finns det ett snabbt sätt att uppdatera många poster i SQL?

  4. MySQL standard datumtid via phpmyadmin