I came across this tutorial to understand how to execute SQL scripts with GO statements.
Now I want to know what can I get the output of the messages TAB.
With several GO statements, the output would be like this:
1 rows affected
912 rows affected
…
But server.ConnectionContext.ExecuteNonQuery() can return only an int, while I need all the text. In case there is some error in some part of query, it should put that also in the output.
Any help would be appreciated.
The easiest thing is possibly to just print the number you get back for
ExecuteNonQuery:This should work, but will not honor the
SET NOCOUNTsetting of the current session/scope.Otherwise you would do it like you would do with “plain” ADO.NET. Don’t use the
ServerConnection.ExecuteNonQuery()method, but create anSqlCommandobject by accessing the underlyingSqlConnectionobject. On that subscribe to theStatementCompletedevent.Using
StatementCompleted(instead, say, manually printing the value thatExecuteNonQuery()returned) has the benefit that it works exactly like SSMS or SQLCMD.EXE would:SET NOCOUNT ONwas set, it will not be called at all.SET NOCOUNT OFFwas set, it will be called for every statement inside a batch.(Sidebar: it looks like
StatementCompletedis exactly what the TDS protocol talks about whenDONE_IN_PROCevent is mentioned; see Remarks of the SET NOCOUNT command on MSDN.)Personally, I have used this approach with success in my own “clone” of SQLCMD.EXE.
UPDATE: It should be noted, that this approach (of course) requires you to manually split the input script/statements at the
GOseparator, because you’re back to usingSqlCommand.Execute*()which cannot handle multiple batches at a time. For this, there are multiple options:GO(caveat:GOcan be called likeGO 5, for example, to execute the previous batch 5 times).I choose the later option, which was quite some work, given that it is not pretty well documented and examples are rare (google a bit, you’ll find some stuff, or use reflector to see how the SMO-Assemblies use that class).
The benefit (and maybe burden) of using the
ManagedBatchParseris, that it will also parse all other constructs of T-SQL scripts (intended forSQLCMD.EXE) for you. Including::setvar,:connect,:quit, etc. You don’t have to implement the respectiveICommandExecutormembers, if your scripts don’t use them, of course. But mind you that you’ll may not be able to execute “arbitrary” scripts.Well, were did that put you. From the “simple question” of how to print “… rows affected” to the fact that it is not trivial to do in a robust and general manner (given the background work required). YMMV, good luck.
Update on ManagedBatchParser Usage
There seems to be no good documenation or example about how to implement
IBatchSource, here is what I went with.And this is how you would use it:
Note that both implementations, either for direct statements (
StatementBatchSource) or for a file (FileBatchSource) have the problem that they read the complete text at onceinto memory. I had one case where that blew up, having a huge(!) script with gazillions of generated
INSERTstatements. Even though I don’t think that is a practical issue,SQLCMD.EXEcould handle it. But for the life of me, I couldn’t figure out how exactly,you would need to form the chunks returned for
IBatchParser.GetContent()so that theparser can still work with them (it looks like they would need to be complete statements,
which would sort of defeat the purpose of the parse in the first place…).