SSMS
 sql >> база данни >  >> Database Tools >> SSMS

SSMS SMO обекти:Вземете резултати от заявка

Най-лесното нещо е възможно просто да отпечатате номера, който получавате обратно за ExecuteNonQuery :

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

Това трябва да работи, но няма да зачита SET NOCOUNT настройка на текущата сесия/обхват.

В противен случай бихте направили това, както бихте направили с "обикновения" ADO.NET. Не използвайте ServerConnection.ExecuteNonQuery() метод, но създайте SqlCommand обект чрез достъп до основния SqlConnection обект. При това се абонирайте за StatementCompleted събитие.

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

Използване на StatementCompleted (вместо, да речем, ръчно отпечатване на стойността, която ExecuteNonQuery() върнати) има предимството, че работи точно както SSMS или SQLCMD.EXE:

  • За команди, които нямат ROWCOUNT, тя изобщо няма да бъде извикана (напр. GO, USE).
  • Ако SET NOCOUNT ON е зададен, той изобщо няма да бъде извикан.
  • Ако SET NOCOUNT OFF е зададен, той ще бъде извикан за всяко изявление в пакет.

(Странична лента:изглежда като StatementCompleted е точно това, за което говори протоколът TDS, когато DONE_IN_PROC събитие е споменато; вижте Забележки на командата SET NOCOUNT в MSDN.)

Лично аз използвах този подход с успех в моя собствен „клонинг“ на SQLCMD.EXE.

АКТУАЛИЗИРАНЕ :Трябва да се отбележи, че този подход (разбира се) изисква ръчно да разделите входния скрипт/изявленията в GO разделител, защото се връщате към използването на SqlCommand.Execute*() който не може да обработва няколко партиди наведнъж. За това има няколко опции:

  • Ръчно разделете въвеждането на редове, започващи с GO (предупреждение:GO може да се извика като GO 5 , например, за да изпълните предишната партида 5 пъти).
  • Използвайте ManagedBatchParser class/library, за да ви помогне да разделите входа на единични пакети, особено внедрете ICommandExecutor.ProcessBatch с кода по-горе (или нещо подобно).

Избирам по-късната опция, която беше доста работа, като се има предвид, че не е много добре документирана и примерите са рядкост (погугъл малко, ще намерите някои неща или използвайте рефлектор, за да видите как SMO-Assemblies използват този клас) .

Ползата (и може би тежестта) от използването на ManagedBatchParser е, че също така ще анализира всички други конструкции на T-SQL скриптове (предназначени за SQLCMD.EXE ) за теб. Включително::setvar , :connect , :quit и т.н. Не е нужно да внедрявате съответния ICommandExecutor членове, ако вашите скриптове не ги използват, разбира се. Но имайте предвид, че може да не сте в състояние да изпълнявате „произволни“ скриптове.

Е, дали това те постави. От „простия въпрос“ как да отпечатате „... засегнати редове“ до факта, че не е тривиално да се направи по стабилен и общ начин (като се има предвид необходимата фонова работа). YMMV, успех.

Актуализация относно използването на ManagedBatchParser

Изглежда, че няма добра документация или пример за това как да се внедри IBatchSource , ето какво минах.

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

И ето как бихте го използвали:

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

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

parser.Parse();

Обърнете внимание, че и двете реализации, или за директни изрази (StatementBatchSource ) или за файл (FileBatchSource ) имат проблем, че четат целия текст наведнъж в паметта. Имах един случай, когато това се взриви, имайки огромен(!) скрипт с милиони генерирани INSERT изявления. Въпреки че не мисля, че това е практически проблем, SQLCMD.EXE можеше да се справи. Но за живота на мен не можах да разбера как точно, ще трябва да формирате късовете, върнати за IBatchParser.GetContent() така че синтактичният анализатор все още може да работи с тях (изглежда, че те ще трябва да бъдат пълни изявления, което на първо място ще попречи на целта на синтактичния анализ...).




  1. DBeaver
  2.   
  3. phpMyAdmin
  4.   
  5. Navicat
  6.   
  7. SSMS
  8.   
  9. MySQL Workbench
  10.   
  11. SQLyog
  1. как да изберете ред с данни от поле за стойност, разделена със запетая

  2. SQL LocalDB - Не може да изтрие DB, когато файловете му са изтрити

  3. Скриптирайте всички съхранени процедури в Management Studio 2005

  4. sql сървър 2012:не може да промени данните за вход sa

  5. Актуализиране на sql битово поле в базата данни