sql >> Database teknologi >  >> Database Tools >> SSMS

SSMS SMO-objekter:Få forespørgselsresultater

Det nemmeste er muligvis bare at udskrive det nummer, du får tilbage for ExecuteNonQuery :

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

Dette burde virke, men vil ikke overholde SET NOCOUNT indstilling af den aktuelle session/omfang.

Ellers ville du gøre det, som du ville gøre med "almindelig" ADO.NET. Brug ikke ServerConnection.ExecuteNonQuery() metode, men opret en SqlCommand objekt ved at få adgang til den underliggende SqlConnection objekt. På det, abonner på StatementCompleted begivenhed.

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

Brug af StatementCompleted (i stedet for f.eks. at udskrive værdien, som ExecuteNonQuery() returneret) har den fordel, at det fungerer præcis som SSMS eller SQLCMD.EXE ville:

  • For kommandoer, der ikke har et ROWCOUNT, bliver det slet ikke kaldt (f.eks. GO, USE).
  • Hvis SET NOCOUNT ON blev indstillet, vil den slet ikke blive kaldt.
  • Hvis SET NOCOUNT OFF blev indstillet, vil det blive kaldt for hver erklæring i en batch.

(Sidebjælke:det ser ud som StatementCompleted er præcis, hvad TDS-protokollen taler om, når DONE_IN_PROC begivenhed er nævnt; se Bemærkninger af kommandoen SET NOCOUNT på MSDN.)

Personligt har jeg brugt denne tilgang med succes i min egen "klone" af SQLCMD.EXE.

OPDATERING :Det skal bemærkes, at denne tilgang (selvfølgelig) kræver, at du manuelt opdeler input-scriptet/udsagn ved GO separator, fordi du er tilbage til at bruge SqlCommand.Execute*() som ikke kan håndtere flere batcher ad gangen. Til dette er der flere muligheder:

  • Opdel manuelt input på linjer, der starter med GO (advarsel:GO kan kaldes som GO 5 , for eksempel for at udføre den forrige batch 5 gange).
  • Brug ManagedBatchParser klasse/bibliotek for at hjælpe dig med at opdele inputtet i enkelte batches, især implementer ICommandExecutor.ProcessBatch med koden ovenfor (eller noget der ligner den).

Jeg vælger den senere mulighed, hvilket var en del arbejde, da det ikke er ret veldokumenteret, og eksempler er sjældne (google lidt, du finder nogle ting, eller brug reflektor for at se, hvordan SMO-samlingerne bruger den klasse) .

Fordelen (og måske byrden) ved at bruge ManagedBatchParser er, at den også vil parse alle andre konstruktioner af T-SQL-scripts (beregnet til SQLCMD.EXE ) for dig. Inklusive::setvar , :connect , :quit osv. Du behøver ikke at implementere den respektive ICommandExecutor medlemmer, hvis dine scripts ikke bruger dem, selvfølgelig. Men husk at du muligvis ikke vil være i stand til at udføre "vilkårlige" scripts.

Tja, hvor satte det dig. Fra det "simple spørgsmål" om, hvordan man udskriver "... berørte rækker" til det faktum, at det ikke er trivielt at gøre på en robust og generel måde (i betragtning af det nødvendige baggrundsarbejde). YMMV, held og lykke.

Opdatering om ManagedBatchParser-brug

Der synes ikke at være nogen god dokumentation eller eksempler på, hvordan man implementerer IBatchSource , her er hvad jeg gik 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;
    }
}

Og sådan ville du bruge det:

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

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

parser.Parse();

Bemærk, at begge implementeringer, enten for direkte sætninger (StatementBatchSource ) eller for en fil (FileBatchSource ) har det problem, at de læser hele teksten ind i hukommelsen på én gang. Jeg havde et tilfælde, hvor det eksploderede, med et enormt(!) script med gazillioner af genereret INSERT udsagn. Selvom jeg ikke tror, ​​det er et praktisk problem, SQLCMD.EXE kunne klare det. Men i mit liv kunne jeg ikke finde ud af, hvordan du præcist skulle danne de bidder, der blev returneret for IBatchParser.GetContent() så parseren stadig kan arbejde med dem (det ser ud til, at de skal være komplette udsagn, hvilket på en måde ville besejre formålet med parsen i første omgang...).




  1. SQL Server / PHP / Web Baseret Admin Tool - Hvad er nogle phpmyadmin-lignende værktøjer til SQL Server?

  2. Jeg kan ikke få adgang til XAMPP phpMyAdmin; der står:Fejl MySQL sagde:Dokumentation Kan ikke oprette forbindelse:ugyldige indstillinger

  3. Hvordan ændrer jeg et SQL-forespørgselsvindues fanenavn i SSMS?

  4. phpMyAdmin 502 dårlig gateway [CentOS7, nginx]