2

Consider the following stored procedure :

create procedure [dbo].[MyTest] ( @p_SqlStatement nvarchar(max) )
as
begin

    exec sp_executesql @p_SqlStatement

    if @@ROWCOUNT = 1
    begin
      select 1;
    end
    else if @@ROWCOUNT <> 1
    begin
      select 0;
    end

end

This stored procedure currently returns 2 datasets, one with the exec sp_executesql @p_SqlStatement data, and the other one would be either 1 or 0. Is there a way to suppress the first dataset? I mean, would it be possible that this stored procedure returns only 1 or 0 ?

I tried adding a RAISERROR( 'MyError', 18, 1 ) right after the exec sp_executesql @p_SqlStatement and then in the catch block select something else, but the first result set is always returned to my stored procedure caller...

Dominic Goulet
  • 7,983
  • 7
  • 28
  • 56
  • Check this question: http://stackoverflow.com/questions/212657/how-do-i-disable-query-results-when-executing-a-stored-procedure-from-a-stored-p – Idan Arye Nov 05 '12 at 20:45
  • Yeah, already saw that, but that only works when you know which columns are being returned by the `exec sp_executesql @p_SqlStatement`, something I do not know. – Dominic Goulet Nov 05 '12 at 20:48
  • Is there always a single select statement in the parameter or can it be multi line SQL code? – Mikael Eriksson Nov 05 '12 at 21:07
  • It will always be a single SQL instruction. It can be a simple `select ...` or can even be a `exec somesp ...` – Dominic Goulet Nov 05 '12 at 21:10
  • @DominicGoulet I added an answer. It will not work with calls to stored procedures. – Mikael Eriksson Nov 05 '12 at 21:28
  • It is actually called by another SP. I did replaced the `select 1` by `return 1` and get the output parameter, but the `sp_executesql` resultset still gets returned to the caller... – Dominic Goulet Nov 05 '12 at 22:07
  • @DominicGoulet: are you still having this issue? There is a rather simple solution to this if you just want to skip the first 1 (or N) result set(s). – Solomon Rutzky Sep 13 '14 at 20:45

4 Answers4

2

You can embed the query in an if exists(.

alter procedure [dbo].[MyTest] ( @p_SqlStatement nvarchar(max) )
as
begin
    set @p_SqlStatement = 'if exists('+@p_SqlStatement+') select 1 else select 0'

    exec sp_executesql @p_SqlStatement
end

There are however some queries where this won't fly.

  • Multiple statements
  • Queries terminated with ;
  • Queries that uses CTE's

There might be more but these are the ones I can think of right now.

Update:

You could try to use openrowset.

alter procedure [dbo].[MyTest] ( @p_SqlStatement nvarchar(max) )
as
begin
  declare @S nvarchar(max)
  set @S = 
    'if exists(
              select *
              from openrowset(
                              ''SQLNCLI'',
                              ''Server=localhost;Trusted_Connection=yes;'',
                              '+quotename(@p_SqlStatement, '''')+'
                             ) as T
              )
      select 1
    else
      select 0'

  exec (@S)
end

I have never used this in productions but from the tests I made i looks like it should work with SP's, CTE's and multiple lines.

You have to allow ad hoc distributed queries.

Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
  • Too much limitations, that won't cut it. The primary use case of my SP is to evaluate `exec` statements, which won't work. – Dominic Goulet Nov 05 '12 at 21:44
  • Sorry, can't use your update either. It assumes localhost is always an available connection name, which is not in many case. Also, as you stated, you have to allow ad hoc distributed queries, which I cannot. – Dominic Goulet Nov 05 '12 at 22:11
0

You may try the NOCOUNT statement ( http://msdn.microsoft.com/en-us/library/ms189837.aspx )

create procedure [dbo].[MyTest] ( @p_SqlStatement nvarchar(max) )
as
begin

SET NOCOUNT ON;

    exec sp_executesql @p_SqlStatement

    if @@ROWCOUNT = 1
    begin
      select 1;
    end
    else if @@ROWCOUNT <> 1
    begin
      select 0;
    end

SET NOCOUNT OFF;

end
naeramarth7
  • 6,030
  • 1
  • 22
  • 26
  • How can the SET NOCOUNT be of any help here? – Dominic Goulet Nov 05 '12 at 21:11
  • 1
    Have you even tried it? Used this method in stored procedures for 3 years in a VB6 / MSSQL2000 environment and it did exactly what you wańt - returning only the last recordset. – naeramarth7 Nov 05 '12 at 21:26
  • But I see your problem - when using nested execution of sps you have to insert whatever the executed procedure returns into a temporary table. See http://dmoonc.com/blog/?p=131 . – naeramarth7 Nov 05 '12 at 21:34
  • 4
    You basically misunderstand NOCOUNT. It simply "Stops the message that shows the count of the number of rows affected" (from MSDN). The reason that it "worked" for you is that your VB6 application only handles single resultsets, which is the last one returned by your SP. You have to explicitely define your connection string using `MultipleActiveResultSets=True` to handle multiple result sets. – Dominic Goulet Nov 05 '12 at 22:09
0

try this approach:

declare @mycount bigint

exec sp_executesql N'select @mycount = count(name) from Page where name like ''P%''', N'@mycount bigint OUTPUT', @mycount OUTPUT

select @mycount

It is important that your statement @p_SqlStatement incorporates a count.

If that is not the case, meaning your want to run this sp for any SQL you come upon, then this is no help. I don't think your can't suppress output of the sp_executesql.

Edit: You could also try this:

declare @mycount bigint

exec sp_executesql N'SELECT * INTO ##MyTempTable from Page where name like ''P%'''

select count(*) from ##MyTempTable 
drop table ##MyTempTable 

This means that you will have to add the following to every query (don't know if this works with sp's?) "SELECT * INTO ##MyTempTable FROM " - That shouldn't be to hard.

"##temptables" are global scoped temptables. Which means that they are also available outside the sp_executesql sp. You must drop the table explicitly.

  • Or you could use this: http://stackoverflow.com/questions/212657/how-do-i-disable-query-results-when-executing-a-stored-procedure-from-a-stored-p sending all your data into temp tables, get the count and produce the output you need. – Per Hoffmann Olsen Nov 06 '12 at 10:57
  • The problem is I don't know which query will be sent. I do not know the number of columns to insert the data into a temp table, and I cannot ensure that there will be a count into my sql statement. – Dominic Goulet Nov 06 '12 at 11:48
  • With the changes above (see Edit:), there is no need for knowing the columns. – Per Hoffmann Olsen Nov 06 '12 at 16:01
  • I cannot know what will be in the `@p_SqlStatement`. You cannot do a `SELECT INTO` when the statement is an `EXECUTE` or `sp_executesql`. – Dominic Goulet Nov 06 '12 at 16:20
  • They can here: http://stackoverflow.com/questions/653714/how-to-select-into-temp-table-from-stored-procedure, but it's not my problem - its yours - im just trying to help. – Per Hoffmann Olsen Nov 06 '12 at 21:30
  • Yes you can with `OPENROWSET`, but that forces you to hardcode the name of your server (or use localhost, local, etc.), but then you assume no SQL Server instance, which I cannot. – Dominic Goulet Nov 07 '12 at 13:02
  • Thanks for your time on the question tho, It helped me on the way to a "temporary" solution. – Dominic Goulet Nov 07 '12 at 13:24
0

I was not able to find any other workaround than using the suggested OPENROWSET. However, I found a way to be independant from the servername / instance. I still have to reconfigure the server to accept ad hoc distributed queries. Here is the final result :

create procedure [dbo].[MyTest] ( @p_SqlStatement nvarchar(max) )
as
begin

    declare @sql nvarchar(max) = N'SELECT * INTO ##TMP FROM OPENROWSET(''SQLOLEDB'',''Server=' + @@SERVERNAME + ';Trusted_Connection=Yes;'',''' + @p_SqlStatement + ''')';
    exec sp_executesql @sql

    if ( select COUNT(1) from ##TMP ) = 1
    begin
      select 1;
    end
    else
    begin
      select 0;
    end

    drop table ##TMP;

end

This solution has it's limitations :

  1. Every column in the @p_SqlStatement must have a name
  2. I have to enable Ad Hoc Distributed Queries on my server.
  3. I have to use @sql variable along with sp_executesql on the OPENROWSET because I was not able to use variables inside OPENROWSET in another way, so this makes dynamic SQL on an OPENROWSET, which is pretty bad performance-wise.

I do reconfigure the server using the following script :

sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
GO
Dominic Goulet
  • 7,983
  • 7
  • 28
  • 56