read DONE_IN_PROC - sql-server

If I understand correctly, with stored procedure result is also returned a message called DONE_IN_PROC
Fragment From MSDN:
... SET NOCOUNT ON eliminates the sending of DONE_IN_PROC messages to the
client for each statement in a stored procedure.
Is there a way to access this message somehow in Management Studio after executing stored procedure and read i.e. number of rows affected.
(I am asking out of curiosity and I know there are other ways to get number of rows affected)

Certain (if not all) Database APIs make the TDS layer's DONE_IN_PROC message accessible. If ODBC is the chosen Database API, see the http://technet.microsoft.com/en-us/library/ms130812.aspx discussion about SQLRowCount, SQLGetStmtAttr, and NOCOUNT.

Late to the party but are you just after the int from the message?
create proc delme as
begin
select * from sys.databases
end
go
set nocount on
go
exec delme --returns "(7 row(s) affected)" on the message channel
select ##ROWCOUNT --returns 7

Related

Why do we use the SET NOCOUNT ON; along with the SELECT ##ROWCOUNT;?

I am learning through the EF Core through the tutorials over here and I come across the following statements being used in the examples of the T-SQL queries:
1 SET NOCOUNT ON; Meaning (from here):
Stops the message that shows the count of the number of rows affected by a Transact-SQL statement or stored procedure from being returned as part of the result set.
2 SELECT ##ROWCOUNT; Meaning (from here)
Returns the number of rows affected by the last statement.
The 1 and 2 are used simultaneously. And that is confusing for me. For instance, here:
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Books] SET [AuthorId] = #p0
WHERE [BookId] = #p1;
SELECT ##ROWCOUNT;
',N'#p1 int,#p0 int',#p1=4,#p0=1
I can not understand why would someone want to turn off the effect of the 2 with the help of 1 if it is possible to avoid the effect of the 2 by just not adding it into the query. I.e. the following query will do exactly the same as the above one:
exec sp_executesql N'UPDATE [Books] SET [AuthorId] = #p0
WHERE [BookId] = #p1;
',N'#p1 int,#p0 int',#p1=4,#p0=1
Am I missing something here or what is the purpose of using both the SET NOCOUNT ON and the SELECT ##ROWCOUNT simultaneously?
SET NOCOUNT ON stops the results from being printed on the console (screen).
The use of ##ROWCOUNT captures the row count as a parameter in T-SQL and makes it possible to use it for further processing. For instance, you could have conditional logic to do something if no rows are updated.
SET NOCOUNT is often used is suppress the DONE_IN_PROC TDS protocol messages (row counts) SQL Server would otherwise return to the client over the underlying TDS protocol stream. These messages can cause issues when using some SQL Server APIs unless the application is specifically coded to handle them (e.g. by invoking ADODB.Recordset.NextRecordSet) to ensure subsequent statements in the batch are executed and result sets returned. Consequently, use of SET NOCOUNT ON has become a common practice with SQL Server unless the row counts are actually needed by the application.
An additional benefit, called out in the doc reference in your question, is a performance benefit in cases where the batch contains many SQL statements. SET NOCOUNT eliminates the overhead of returning the messages that would be discarded anyway.

SET NOCOUNT ON brings back messages in SQL Server Management Studio

I have the following stored procedure:
ALTER PROCEDURE [dbo].[spTitle_GetTitleById]
(
#TitleId INT
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
Id,
Name,
Active
FROM
Title
WHERE
Id = #TitleId
END
I was told to use SET NOCOUNT ON; if I don't want messages to be returned. I ran this stored procedure through SQL Server Management Studio 2008 and I got the following message:
(1 row(s) affected)
This is still a message. One of our DBAs said that this will be the case, but when it is run through an application it will not return any messages. Is there a way that I can test to see if messages were returned or not when I use SET NOCOUNT ON; I don't want to assume, I want to know.
I right clicked the stored procedure and selected Execute Stored Procedure... I then set it to OFF, and I got:
(1 row(s) affected)
(1 row(s) affected)
So setting it to ON or OFF it still brought back messages in the Messages tab in the results panel.
Just another question, when will it be wise (in what scenarios) to use SET NOCOUNT OFF;?
SET NOCOUNT ON is reset when the procedure exits and it goes up the call stack. When you execute the procedure from SSMS it generates a script like the following.
DECLARE #return_value int
EXEC #return_value = [dbo].[spTitle_GetTitleById]
#TitleId = 1
SELECT 'Return Value' = #return_value /*Message comes from here*/
If youi wanted to avoid that for some reason you would need to SET NOCOUNT ON in the outer batch. See SET NOCOUNT ON usage for some discussion about the merits of having this ON or OFF
Just another question, when will it be wise (in what scenarios) to use SET NOCOUNT OFF?
See What are the advantages and disadvantages of turning NOCOUNT off in SQL Server queries? For the benefits turning SET NOCOUNT ON
As for why you would want to turn this off (so that rowcounts are returned) - you need this off whenever you want to be able to tell how many rows were affected in situations where there is no resultset, or you wish to be able to get a rowcount without first reading through the entire resultset.
For example in .Net the DataAdapter class uses rowcounts and so setting NOCOUNT ON causes issues when editing or deleting data (source).
That is not correct, script out the proc an make sure it is not OFF instead o ON, if it is ON it should not return (1 row(s) affected) messages
Also how are you executing the proc
is is just this
exec spTitle_GetTitleById 1

Errors: "INSERT EXEC statement cannot be nested." and "Cannot use the ROLLBACK statement within an INSERT-EXEC statement." How to solve this?

I have three stored procedures Sp1, Sp2 and Sp3.
The first one (Sp1) will execute the second one (Sp2) and save returned data into #tempTB1 and the second one will execute the third one (Sp3) and save data into #tempTB2.
If I execute the Sp2 it will work and it will return me all my data from the Sp3, but the problem is in the Sp1, when I execute it it will display this error:
INSERT EXEC statement cannot be nested
I tried to change the place of execute Sp2 and it display me another error:
Cannot use the ROLLBACK statement
within an INSERT-EXEC statement.
This is a common issue when attempting to 'bubble' up data from a chain of stored procedures. A restriction in SQL Server is you can only have one INSERT-EXEC active at a time. I recommend looking at How to Share Data Between Stored Procedures which is a very thorough article on patterns to work around this type of problem.
For example a work around could be to turn Sp3 into a Table-valued function.
This is the only "simple" way to do this in SQL Server without some giant convoluted created function or executed sql string call, both of which are terrible solutions:
create a temp table
openrowset your stored procedure data into it
EXAMPLE:
INSERT INTO #YOUR_TEMP_TABLE
SELECT * FROM OPENROWSET ('SQLOLEDB','Server=(local);TRUSTED_CONNECTION=YES;','set fmtonly off EXEC [ServerName].dbo.[StoredProcedureName] 1,2,3')
Note: You MUST use 'set fmtonly off', AND you CANNOT add dynamic sql to this either inside the openrowset call, either for the string containing your stored procedure parameters or for the table name. Thats why you have to use a temp table rather than table variables, which would have been better, as it out performs temp table in most cases.
OK, encouraged by jimhark here is an example of the old single hash table approach: -
CREATE PROCEDURE SP3 as
BEGIN
SELECT 1, 'Data1'
UNION ALL
SELECT 2, 'Data2'
END
go
CREATE PROCEDURE SP2 as
BEGIN
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
INSERT INTO #tmp1
EXEC SP3
else
EXEC SP3
END
go
CREATE PROCEDURE SP1 as
BEGIN
EXEC SP2
END
GO
/*
--I want some data back from SP3
-- Just run the SP1
EXEC SP1
*/
/*
--I want some data back from SP3 into a table to do something useful
--Try run this - get an error - can't nest Execs
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
DROP TABLE #tmp1
CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))
INSERT INTO #tmp1
EXEC SP1
*/
/*
--I want some data back from SP3 into a table to do something useful
--However, if we run this single hash temp table it is in scope anyway so
--no need for the exec insert
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1'))
DROP TABLE #tmp1
CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20))
EXEC SP1
SELECT * FROM #tmp1
*/
My work around for this problem has always been to use the principle that single hash temp tables are in scope to any called procs. So, I have an option switch in the proc parameters (default set to off). If this is switched on, the called proc will insert the results into the temp table created in the calling proc. I think in the past I have taken it a step further and put some code in the called proc to check if the single hash table exists in scope, if it does then insert the code, otherwise return the result set. Seems to work well - best way of passing large data sets between procs.
This trick works for me.
You don't have this problem on remote server, because on remote server, the last insert command waits for the result of previous command to execute. It's not the case on same server.
Profit that situation for a workaround.
If you have the right permission to create a Linked Server, do it.
Create the same server as linked server.
in SSMS, log into your server
go to "Server Object
Right Click on "Linked Servers", then "New Linked Server"
on the dialog, give any name of your linked server : eg: THISSERVER
server type is "Other data source"
Provider : Microsoft OLE DB Provider for SQL server
Data source: your IP, it can be also just a dot (.), because it's localhost
Go to the tab "Security" and choose the 3rd one "Be made using the login's current security context"
You can edit the server options (3rd tab) if you want
Press OK, your linked server is created
now your Sql command in the SP1 is
insert into #myTempTable
exec THISSERVER.MY_DATABASE_NAME.MY_SCHEMA.SP2
Believe me, it works even you have dynamic insert in SP2
I found a work around is to convert one of the prods into a table valued function. I realize that is not always possible, and introduces its own limitations. However, I have been able to always find at least one of the procedures a good candidate for this. I like this solution, because it doesn't introduce any "hacks" to the solution.
I encountered this issue when trying to import the results of a Stored Proc into a temp table, and that Stored Proc inserted into a temp table as part of its own operation. The issue being that SQL Server does not allow the same process to write to two different temp tables at the same time.
The accepted OPENROWSET answer works fine, but I needed to avoid using any Dynamic SQL or an external OLE provider in my process, so I went a different route.
One easy workaround I found was to change the temporary table in my stored procedure to a table variable. It works exactly the same as it did with a temp table, but no longer conflicts with my other temp table insert.
Just to head off the comment I know that a few of you are about to write, warning me off Table Variables as performance killers... All I can say to you is that in 2020 it pays dividends not to be afraid of Table Variables. If this was 2008 and my Database was hosted on a server with 16GB RAM and running off 5400RPM HDDs, I might agree with you. But it's 2020 and I have an SSD array as my primary storage and hundreds of gigs of RAM. I could load my entire company's database to a table variable and still have plenty of RAM to spare.
Table Variables are back on the menu!
I recommend to read this entire article. Below is the most relevant section of that article that addresses your question:
Rollback and Error Handling is Difficult
In my articles on Error and Transaction Handling in SQL Server, I suggest that you should always have an error handler like
BEGIN CATCH
IF ##trancount > 0 ROLLBACK TRANSACTION
EXEC error_handler_sp
RETURN 55555
END CATCH
The idea is that even if you do not start a transaction in the procedure, you should always include a ROLLBACK, because if you were not able to fulfil your contract, the transaction is not valid.
Unfortunately, this does not work well with INSERT-EXEC. If the called procedure executes a ROLLBACK statement, this happens:
Msg 3915, Level 16, State 0, Procedure SalesByStore, Line 9 Cannot use the ROLLBACK statement within an INSERT-EXEC statement.
The execution of the stored procedure is aborted. If there is no CATCH handler anywhere, the entire batch is aborted, and the transaction is rolled back. If the INSERT-EXEC is inside TRY-CATCH, that CATCH handler will fire, but the transaction is doomed, that is, you must roll it back. The net effect is that the rollback is achieved as requested, but the original error message that triggered the rollback is lost. That may seem like a small thing, but it makes troubleshooting much more difficult, because when you see this error, all you know is that something went wrong, but you don't know what.
I had the same issue and concern over duplicate code in two or more sprocs. I ended up adding an additional attribute for "mode". This allowed common code to exist inside one sproc and the mode directed flow and result set of the sproc.
what about just store the output to the static table ? Like
-- SubProcedure: subProcedureName
---------------------------------
-- Save the value
DELETE lastValue_subProcedureName
INSERT INTO lastValue_subProcedureName (Value)
SELECT #Value
-- Return the value
SELECT #Value
-- Procedure
--------------------------------------------
-- get last value of subProcedureName
SELECT Value FROM lastValue_subProcedureName
its not ideal, but its so simple and you don't need to rewrite everything.
UPDATE:
the previous solution does not work well with parallel queries (async and multiuser accessing) therefore now Iam using temp tables
-- A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished.
-- The table can be referenced by any nested stored procedures executed by the stored procedure that created the table.
-- The table cannot be referenced by the process that called the stored procedure that created the table.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NULL
CREATE TABLE #lastValue_spGetData (Value INT)
-- trigger stored procedure with special silent parameter
EXEC dbo.spGetData 1 --silent mode parameter
nested spGetData stored procedure content
-- Save the output if temporary table exists.
IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NOT NULL
BEGIN
DELETE #lastValue_spGetData
INSERT INTO #lastValue_spGetData(Value)
SELECT Col1 FROM dbo.Table1
END
-- stored procedure return
IF #silentMode = 0
SELECT Col1 FROM dbo.Table1
Declare an output cursor variable to the inner sp :
#c CURSOR VARYING OUTPUT
Then declare a cursor c to the select you want to return.
Then open the cursor.
Then set the reference:
DECLARE c CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
SELECT ...
OPEN c
SET #c = c
DO NOT close or reallocate.
Now call the inner sp from the outer one supplying a cursor parameter like:
exec sp_abc a,b,c,, #cOUT OUTPUT
Once the inner sp executes, your #cOUT is ready to fetch. Loop and then close and deallocate.
If you are able to use other associated technologies such as C#, I suggest using the built in SQL command with Transaction parameter.
var sqlCommand = new SqlCommand(commandText, null, transaction);
I've created a simple Console App that demonstrates this ability which can be found here:
https://github.com/hecked12/SQL-Transaction-Using-C-Sharp
In short, C# allows you to overcome this limitation where you can inspect the output of each stored procedure and use that output however you like, for example you can feed it to another stored procedure. If the output is ok, you can commit the transaction, otherwise, you can revert the changes using rollback.
On SQL Server 2008 R2, I had a mismatch in table columns that caused the Rollback error. It went away when I fixed my sqlcmd table variable populated by the insert-exec statement to match that returned by the stored proc. It was missing org_code. In a windows cmd file, it loads result of stored procedure and selects it.
set SQLTXT= declare #resets as table (org_id nvarchar(9), org_code char(4), ^
tin(char9), old_strt_dt char(10), strt_dt char(10)); ^
insert #resets exec rsp_reset; ^
select * from #resets;
sqlcmd -U user -P pass -d database -S server -Q "%SQLTXT%" -o "OrgReport.txt"

sql server send output messages by email

I have been running up a stored procedure that runs several separate update and insert statements. It also prints some status messages that I can see in sql management studio. I am going to automate this procedure to run overnight but need the output as a final check to be sent to me.
I know how to use the email stored procedure and have that working but can't find where the message output i.e. (10 rows affected)... is all stored.
Informational messages like '10 rows affected' or PRINT output are sent straight to the client and you can't capture it in Transact-SQL. You can, however, be the client if you are a CLR procedure, and in that case you will get all the output from the procedure you invoked. Requires SQL Server 2005 or newer for CLR.
A much better alternative is to have the procedure create a true output, like a rowset result or output parameters.
you can always store the output in a table
exec master..xp_sendmail
#recipients = 'you#you.com',
#subject = 'my alert'.
#query = 'select * from mytable'
you can't get information messages from t-sql.
PRINT #WIDELOAD

Disable messages in SQL2008 result set

further to these two questions, is there a way to disable the messages that may get sent along with the resultset in SQL2008?
(please note this is nothing to do with the ANSI_WARNINGS setting. Or NOCOUNT.)
Thanks for any help.
Edit: It's not a problem with compatibility settings or table owners. And it's nothing to do with NOCOUNT. Trust me.
No, there's not a way to disable all messages that get sent along with the result sets. Set nocount on/off doesn't have an effect on these types of messages.
You need the NOCOUNT in the body of the Sproc anyway (I appreciate that you've tested it with and without)
In circumstances like this I get the actual call to the Sproc (either from a Debug in my APP, or using SQL Profiler) and then plug that into SSMS or whatever IDE you use, wrapping it in a ROLLBACK transaction (so it can't accidentally make any changes). Note: Log on to SQL Server, with your IDE, using the same credentials as the App will use.
BEGIN TRANSACTION
EXEC StaffEnquirySurnameSearch #searchterm = 'FOOBAR'
ROLLBACK
and see what you get. Use TEXT mode for output, rather than GRID mode which might hide something
Just to show how I think NOCOUNT shoud be added to your SProc:
CREATE PROCEDURE StaffEnquirySurnameSearch
#searchterm varchar(255)
AS
SET NOCOUNT ON
SELECT AD.Name, AD.Company, AD.telephoneNumber, AD.manager, CVS.Position,
CVS.CompanyArea, CVS.Location, CVS.Title, AD.guid AS guid,
AD.firstname, AD.surname
FROM ADCVS AD
LEFT OUTER JOIN CVS ON
AD.Guid=CVS.Guid
WHERE AD.SurName LIKE #searchterm
ORDER BY AD.Surname, AD.Firstname
GO
I note that you are not prefixing the tables with a database owner (most commonly "dbo") which might mean that there are additional copies owned by whomever and that they turn out to be the default from the applications permissions perspective, although I don't think that will change the resultsets [between SQL versions], However, same thing applies to ownership of the Sproc, and there you might be calling some earlier version, created for a different owner.
Ditto where your Sproc name is defined in your ASP.NET code (which I can't seem to find in your linked question) should also have the owner defined, i.e.
EXEC dbo.StaffEnquirySurnameSearch #searchterm = 'FOOBAR'
Did you change the compatibility level when you upgraded from SQL 2000 to 2008? If it is some sort of backward compatibility warning message that might cure it.
Have you tried running the same CONTAINS query without the "OR"?
i.e.:
SELECT * FROM my_table
WHERE CONTAINS(my_column, 'a monkey') -- "a" is a noise word
instead of
SELECT * FROM my_table
WHERE CONTAINS(my_column, 'a OR monkey') -- "a" is a noise word
You can wrap it in a try catch... more info in books online
For example:
CREATE TABLE Test_ShortString(
ShortString varchar(10) NULL
)
begin Try
insert into
Test_ShortString (ShortString)
values ('123456789012345')
End Try
Begin catch
--Select Error_Number() as ErrorNumber
end catch

Resources