SET statement always runs regardless of IF ELSE condition - sql

I'm losing my sanity over this SQL code. The below IF ELSE statement functions correctly when testing with simple PRINT statement, i.e. the table does not exist so it prints 'FALSE'. But when I uncomment the SET statement and execute, it tries to run the SET statement, and naturally gives and error since the table does not exist.
DECLARE #zeus_calls310_counter int;
IF EXISTS (SELECT * FROM [zeus].tpza.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'calls_310')
BEGIN
--SET #zeus_calls310_counter = (SELECT COUNT(*) FROM [zeus].[tpza].[dbo].[calls_310]);
PRINT 'TRUE'
END
ELSE
BEGIN
PRINT 'FALSE';
END

Your title indicates where your understanding goes wrong. In the case where the table does not exist, it is not that the SET statement runs and gives an error; it is that when the statement is parsed there is an error.
Whenever you run some SQL, the server first parses the statement, and then, if parsing succeeds, runs it. So you cannot have bare SQL that depends on the existence of tables! Your SET statement will be parsed whether or not the table exists - so when it doesn't, parsing fails.
One solution to this is to wrap those statements that depend on objects that may or may not exist within EXEC. However, in this case you want to populate a variable with the result of a query on that table, and inside an EXEC that variable will not be in scope. So we'd need more detail on what you're doing to do with #zeus_calls310_counter - if it's going to be used soon after, perhaps you could wrap the whole thing, including its declaration, in an EXEC.

#AAkashM is on the right track.
If you use EXEC sp_executesql you can also specify OUTPUT parameters, which in your case would contain the result of the SELECT COUNT statement.

Try this :
DECLARE #zeus_calls310_counter int;
IF EXISTS (SELECT * FROM [zeus].tpza.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'calls_310')
BEGIN
SET #zeus_calls310_counter = (SELECT COUNT(*) FROM [zeus].[tpza].[calls_310]);
PRINT 'TRUE'
END
ELSE
BEGIN
PRINT 'FALSE';
END

Use this for checking if table exists
IF OBJECT_ID(N'[zeus].[table_name]', N'U') IS NULL
BEGIN
--
END
ELSE
BEGIN
--
END

Related

Properly understanding the error Cannot use the ROLLBACK statement within an INSERT-EXEC statement - Msg 50000, Level 16, State 1

I understand there is a regularly quoted answer that is meant to address this question, but I believe there is not enough explanation on that thread to really answer the question.
Why earlier answers are inadequate
The first (and accepted) answer simply says this is a common problem and talks about having only one active insert-exec at a time (which is only the first half of the question asked there and doesn't address the ROLLBACK error). The given workaround is to use a table-valued function - which does not help my scenario where my stored procedure needs to update data before returning a result set.
The second answer talks about using openrowset but notes you cannot dynamically specify argument values for the stored procedure - which does not help my scenario because different users need to call my procedure with different parameters.
The third answer provides something called "the old single hash table approach" but does not explain whether it is addressing part 1 or 2 of the question, nor how it works, nor why.
No answer explains why the database is giving this error in the first place.
My use case / requirements
To give specifics for my scenario (although simplified and generic), I have procedures something like below.
In a nutshell though - the first procedure will return a result set, but before it does so, it updates a status column. Effectively these records represent records that need to be synchronised somewhere, so when you call this procedure the procedure will flag the records as being "in progress" for sync.
The second stored procedure calls that first one. Of course the second stored procedure wants to take those records and perform inserts and updates on some tables - to keep those tables in sync with whatever data was returned from the first procedure. After performing all the updates, the second procedure then calls a third procedure - within a cursor (ie. row by row on all the rows in the result set that was received from the first procedure) - for the purpose of setting the status on the source data to "in sync". That is, one by one it goes back and says "update the sync status on record id 1, to 'in sync'" ... and then record 2, and then record 3, etc.
The issue I'm having is that calling the second procedure results in the error
Msg 50000, Level 16, State 1, Procedure getValuesOuterCall, Line 484 [Batch Start Line 24]
Cannot use the ROLLBACK statement within an INSERT-EXEC statement.
but calling the first procedure directly causes no error.
Procedure 1
-- Purpose here is to return a result set,
-- but for every record in the set we want to set a status flag
-- to another value as well.
alter procedure getValues #username, #password, #target
as
begin
set xact_abort on;
begin try
begin transaction;
declare #tableVariable table (
...
);
update someOtherTable
set something = somethingElse
output
someColumns
into #tableVariable
from someTable
join someOtherTable
join etc
where someCol = #username
and etc
;
select
someCols
from #tableVariable
;
commit;
end try
begin catch
if ##trancount > 0 rollback;
declare #msg nvarchar(2048) = error_message() + ' Error line: ' + CAST(ERROR_LINE() AS nvarchar(100));
raiserror (#msg, 16, 1);
return 55555
end catch
end
Procedure 2
-- Purpose here is to obtain the result set from earlier procedure
-- and then do a bunch of data updates based on the result set.
-- Lastly, for each row in the set, call another procedure which will
-- update that status flag to another value.
alter procedure getValuesOuterCall #username, #password, #target
as
begin
set xact_abort on;
begin try
begin transaction;
declare #anotherTableVariable
insert into #anotherTableVariable
exec getValues #username = 'blah', #password = #somePass, #target = ''
;
with CTE as (
select someCols
from #anotherTableVariable
join someOtherTables, etc;
)
merge anUnrelatedTable as target
using CTE as source
on target.someCol = source.someCol
when matched then update
target.yetAnotherCol = source.yetAnotherCol,
etc
when not matched then
insert (someCols, andMoreCols, etc)
values ((select someSubquery), source.aColumn, source.etc)
;
declare #myLocalVariable int;
declare #mySecondLocalVariable int;
declare lcur_myCursor cursor for
select keyColumn
from #anotherTableVariable
;
open lcur_muCursor;
fetch lcur_myCursor into #myLocalVariable;
while ##fetch_status = 0
begin
select #mySecondLocalVariable = someCol
from someTable
where someOtherCol = #myLocalVariable;
exec thirdStoredProcForSettingStatusValues #id = #mySecondLocalVariable, etc
end
deallocate lcur_myCursor;
commit;
end try
begin catch
if ##trancount > 0 rollback;
declare #msg nvarchar(2048) = error_message() + ' Error line: ' + CAST(ERROR_LINE() AS nvarchar(100));
raiserror (#msg, 16, 1);
return 55555
end catch
end
The parts I don't understand
Firstly, I have no explicit 'rollback' (well, except in the catch block) - so I have to presume that an implicit rollback is causing the issue - but it is difficult to understand where the root of this problem is; I am not even entirely sure which stored procedure is causing the issue.
Secondly, I believe the statements to set xact_abort and begin transaction are required - because in procedure 1 I am updating data before returning the result set. In procedure 2 I am updating data before I call a third procedure to update further data.
Thirdly, I don't think procedure 1 can be converted to a table-valued function because the procedure performs a data update (which would not be allowed in a function?)
Things I have tried
I removed the table variable from procedure 2 and actually created a permanent table to store the results coming back from procedure 1. Before calling procedure 1, procedure 2 would truncate the table. I still got the rollback error.
I replaced the table variable in procedure 1 with a temporary table (ie. single #). I read the articles about how such a table persists for the lifetime of the connection, so within procedure 1 I had drop table if exists... and then create table #.... I still got the rollback error.
Lastly
I still don't understand exactly what is the problem - what is Microsoft struggling to accomplish here? Or what is the scenario that SQL Server cannot accommodate for a requirement that appears to be fairly straightforward: One procedure returns a result set. The calling procedure wants to perform actions based on what's in that result set. If the result set is scoped to the first procedure, then why can't SQL Server just create a temporary copy of the result set within the scope of the second procedure so that it can be acted upon?
Or have I missed it completely and the issue has something to do with the final call to a third procedure, or maybe to do with using try ... catch - for example, perhaps the logic is totally fine but for some reason it is hitting the catch block and the rollback there is the problem (ie. so if I fix the underlying reason leading us to the catch block, all will resolve)?

SQL IF Statement to Insert Into the same table

I have a need to query a data set that is stored in two different SQL schemas depending on how we receive the data. I have created the two queries to look at dataset 1 and dataset 2 and it formats that data into two separate temp tables (in the same format).
I'm now trying to do the second step which is working out which temp table has been populated putting this data into a new temp table in order to move on to step 3.
To do this, I'm trying to create an IF statement to work out which dataset we receive but can't seem to get the query to work (even though I have seen solutions from other people's similar queries).
This is my code:
IF object_id('tempdb..#Final_Prem') IS NOT NULL
BEGIN
DROP TABLE #Final_Prem
END
DECLARE #Command varchar(500)
DECLARE #DS1 AS FLOAT
DECLARE #DS2 AS FLOAT
SET #DS1 = (SELECT SUM(PREM) FROM #Dataset1 )
SET #DS2 = (SELECT SUM(PREM) FROM #Dataset2 )
IF (#DS1 IS NULL OR #DS1 = 0)
BEGIN
SET #Command = 'SELECT * INTO #Final_Prem FROM #Dataset2'
END ELSE BEGIN
SET #Command = 'SELECT * INTO #Final_Prem FROM #Dataset1'
END
EXECUTE (#Command)
SELECT * FROM #Final_Prem
The error I keep getting is
Msg 208, Level 16, State 0, Line 18
Invalid object name '#Final_Prem'
Any help would be much appreciated, thanks
You cannot use single-hash tables in dynamic queries. The session that runs the dynamic query is different than the outside one, and thus has no access to it.
If you are sure you will be running the proc/query once each time, you can change the # table to a ##table.
If you want to use both dynamic sql and concurrent runnings, you can save ##SPID into a variable and use it on the end of the ## table names.
The query as a whole needs to compile before any of it can run, and it looks like the last line SELECT * FROM #Final_Prem is causing compilation to fail because the #Final_Prem table does not (yet) exist at that stage.
If you are using SSMS, then you can fix it by putting a line containing only GO after the EXECUTE statement. GO is a pseudo statement (not part of the SQL language) that chops the input into batches( pieces), which are compiled and run one by one, thus preventing such compilation issues.
You're having a scope issue.
A temporary table is only visible to the session where it's created. Your table #Final_Prem is created within the scope of the EXECUTE (#Command) statement, which, unfortunately, is it's own session, and is different from the scope of the calling procedure. As soon as that EXECUTE() is done, the temp table is dropped.
You have two options.
Create a real table.
Use a global temporary table (##Final_Prem instead of
#Final_Prem).
Either way, the table will be accessible from both the inner and outer sessions.
Edit (response to comment): Code in comments is pretty hard to read, so here's what the code would look like using a global temp table:
IF object_id('tempdb..##Final_Prem') IS NOT NULL BEGIN DROP TABLE ##Final_Prem END
DECLARE #Command varchar(500)
DECLARE #DS1 AS FLOAT
DECLARE #DS2 AS FLOAT
SET #DS1 = (SELECT SUM(PREM) FROM ##Dataset1 )
SET #DS2 = (SELECT SUM(PREM) FROM ##Dataset2 )
IF (#DS1 IS NULL OR #DS1 = 0)
BEGIN
SET #Command = 'SELECT * INTO ##Final_Prem FROM ##Dataset2'
END ELSE BEGIN3
SET #Command = 'SELECT * INTO ##Final_Prem FROM ##Dataset1'
END
EXECUTE (#Command)
SELECT * FROM ##Final_Prem
I also changed the two Dataset tables to globals, because they're both accessed from both sessions, too. So you'd need to change them elsewhere, since they're not defined within this code block.
You can simply place the select statement inside the dynamic SQL too like below:
IF (#DS1 IS NULL OR #DS1 = 0)
BEGIN
SET #Command = 'SELECT * INTO #Final_Prem FROM #Dataset2 SELECT * FROM #Final_Prem'
END ELSE BEGIN
SET #Command = 'SELECT * INTO #Final_Prem FROM #Dataset1 SELECT * FROM #Final_Prem'
END
EXECUTE (#Command)
Please check the db<>fiddle here.

Issue with RAW SQL passed into a Function as an Argument to check if Data Exists

I cannot touch the VB Code to make this part easier on myself. It's handled by a very small number of Devs out of State. So this is a SQL issue only.
I know I could make the change in VB (so don't throw that as a solution) but, us Analysts can't. It's a multi billion dollar Corp and changes like this would go through a rigorous Approval process and would probably be denied.
But, we have the freedom to make Custom Tables/Fields [in the Application] and Edits on a DB Level.
My Goal is to Create a function that Grabs RAW SQL from a Field on the Front End and pass it through as an Argument to prove if it's valid to "Print a Document". It returns True or False. If True, the document prints.
Example:
The Script: This will take the SQL as hmy from property where scode = '26thst' in the Argument.
The Function: Grabs the SQL and just needs to check if the SQL Exists and pass back True or False
I have made the Function and it's legal but, does not seem to produce results. The Document gets skipped. I'm kind of stumped on an OOP process for SQL to prove True/False on an Execute #SQL
CASE
WHEN ISNULL(RS1.sdocument1, '') <> '' AND RS1.balwaysprint1 = 'NO'
AND NYSQLFUNCTION(RS1.SSQL1) = 'TRUE' THEN RS1.RS1.DOCUMENT1
WHEN ISNULL(RS1.sdocument1, '') <> '' AND RS1.balwaysprint1 = 'YES'
THEN RS1.RS1.DOCUMENT1
END "_FILE_1"
CREATE FUNCTION NYSQLFUNCTION(#SQLTEXT NVARCHAR(MAX))
RETURNS VARCHAR(500)AS BEGIN
DECLARE #FILEPRINT AS VARCHAR(5)
IF EXISTS(SELECT #SQLTEXT)
SET #FILEPRINT = 'TRUE'
ELSE
SET #FILEPRINT = 'FALSE'
RETURN #FILEPRINT END;
NOTE! When you are calling a function you generally need to include the schema (default dbo)... so dbo.NYSQLFUNCTION
Also... that function will always return 'TRUE' as far as I can see.
If you want to at least check that some SQL has been supplied you could do this...
ie:
CREATE FUNCTION NYSQLFUNCTION(#SQLTEXT NVARCHAR(MAX))
RETURNS VARCHAR(500) AS
BEGIN
DECLARE #FILEPRINT AS VARCHAR(500)
IF #SQLTEXT IS NOT NULL AND LEN(#SQLTEXT) > 0
SET #FILEPRINT = 'TRUE'
ELSE
SET #FILEPRINT = 'FALSE'
RETURN #FILEPRINT
END;
If you actually want to execute the SQL supplied then you'll want to change the function to do that.
... but you can't call sp_executeSQL to dynamically execute the SQL from within a function (because its a stored procedure.. see Execute Stored Procedure from a Function for more info)
So you can do one of the hideous hacks in that answer above...
Or... you might be able to use a Stored procedure instead of a function (depends on the code calling it)
Or... if you know the query is always going to be asking for entries from the same table you could just parse the text for the parameters. EG.... I have assumed that the SQLTEXT will ALWAYS be asking for an entry from the table 'property' with a where clause of scode = 'something'
CREATE FUNCTION NYSQLFUNCTION(#SQLTEXT NVARCHAR(MAX))
RETURNS VARCHAR(500) AS
BEGIN
DECLARE #FILEPRINT AS VARCHAR(500) = 'FALSE'
-- eg.... #SQLTEXT should be something like.... hmy from property where scode = '26thst'
DECLARE #scode NVARCHAR(500)
DECLARE #scodeStartIndex INT, #sCodeEndIndex INT
SELECT #scodeStartIndex = CHARINDEX(' scode ', #SQLTEXT)
IF #scodeStartIndex > 0
BEGIN
SELECT #scodeStartIndex = CHARINDEX('''', #SQLTEXT, #scodeStartIndex)
IF #scodeStartIndex > 0
BEGIN
SELECT #sCodeEndIndex = CHARINDEX('''', #SQLTEXT, #scodeStartIndex + 1)
IF #sCodeEndIndex > 0
BEGIN
SELECT #scode = SUBSTRING(#SQLTEXT, #scodeStartIndex + 1, #sCodeEndIndex - (#scodeStartIndex + 1))
IF EXISTS (SELECT NULL FROM [property] WHERE scode = #scode)
BEGIN
SELECT #FILEPRINT = 'TRUE'
END
END
END
END
RETURN #FILEPRINT
END;
Or... one last option available to you (depending upon your ability to modify the database) is to create a CLR function that executes the supplied SQL query and returns true/false. You would be able to call a CLR function within your user defined function. Here's an article that might help: https://www.codeproject.com/Articles/1098543/Use-SQL-CLR-Function-to-Overcome-Limitation-of-SQL

TSQL - How to use GO inside of a BEGIN .. END block?

I am generating a script for automatically migrating changes from multiple development databases to staging/production. Basically, it takes a bunch of change-scripts, and merges them into a single script, wrapping each script in a IF whatever BEGIN ... END statement.
However, some of the scripts require a GO statement so that, for instance, the SQL parser knows about a new column after it's created.
ALTER TABLE dbo.EMPLOYEE
ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO -- Necessary, or next line will generate "Unknown column: EMP_IS_ADMIN"
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
However, once I wrap that in an IF block:
IF whatever
BEGIN
ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
END
It fails because I am sending a BEGIN with no matching END. However, if I remove the GO it complains again about an unknown column.
Is there any way to create and update the same column within a single IF block?
I had the same problem and finally managed to solve it using SET NOEXEC.
IF not whatever
BEGIN
SET NOEXEC ON;
END
ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
SET NOEXEC OFF;
GO is not SQL - it is simply a batch separator used in some MS SQL tools.
If you don't use that, you need to ensure the statements are executed separately - either in different batches or by using dynamic SQL for the population (thanks #gbn):
IF whatever
BEGIN
ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL;
EXEC ('UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever')
END
You could try sp_executesql, splitting the contents between each GO statement into a separate string to be executed, as demonstrated in the example below. Also, there is a #statementNo variable to track which statement is being executed for easy debugging where an exception occurred. The line numbers will be relative to the beginning of the relevant statement number that caused the error.
BEGIN TRAN
DECLARE #statementNo INT
BEGIN TRY
IF 1=1
BEGIN
SET #statementNo = 1
EXEC sp_executesql
N' ALTER TABLE dbo.EMPLOYEE
ADD COLUMN EMP_IS_ADMIN BIT NOT NULL'
SET #statementNo = 2
EXEC sp_executesql
N' UPDATE dbo.EMPLOYEE
SET EMP_IS_ADMIN = 1'
SET #statementNo = 3
EXEC sp_executesql
N' UPDATE dbo.EMPLOYEE
SET EMP_IS_ADMIN = 1x'
END
END TRY
BEGIN CATCH
PRINT 'Error occurred on line ' + cast(ERROR_LINE() as varchar(10))
+ ' of ' + 'statement # ' + cast(#statementNo as varchar(10))
+ ': ' + ERROR_MESSAGE()
-- error occurred, so rollback the transaction
ROLLBACK
END CATCH
-- if we were successful, we should still have a transaction, so commit it
IF ##TRANCOUNT > 0
COMMIT
You can also easily execute multi-line statements, as demonstrated in the example above, by simply wrapping them in single quotes ('). Don't forget to escape any single quotes contained inside the string with a double single-quote ('') when generating the scripts.
You can enclose the statements in BEGIN and END instead of the GO inbetween
IF COL_LENGTH('Employees','EMP_IS_ADMIN') IS NULL --Column does not exist
BEGIN
BEGIN
ALTER TABLE dbo.Employees ADD EMP_IS_ADMIN BIT
END
BEGIN
UPDATE EMPLOYEES SET EMP_IS_ADMIN = 0
END
END
(Tested on Northwind database)
Edit: (Probably tested on SQL2012)
I ultimately got it to work by replacing every instance of GO on its own line with
END
GO
---Automatic replacement of GO keyword, need to recheck IF conditional:
IF whatever
BEGIN
This is greatly preferable to wrapping every group of statements in a string, but is still far from ideal. If anyone finds a better solution, post it and I'll accept it instead.
You may try this solution:
if exists(
SELECT...
)
BEGIN
PRINT 'NOT RUN'
RETURN
END
--if upper code not true
ALTER...
GO
UPDATE...
GO
I have used RAISERROR in the past for this
IF NOT whatever BEGIN
RAISERROR('YOU''RE ALL SET, and sorry for the error!', 20, -1) WITH LOG
END
ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
You can incorporate a GOTO and LABEL statements to skip over code, thus leaving the GO keywords intact.

How to Suppress the SELECT Output of a Stored Procedure called from another Stored Procedure in SQL Server?

I'm not talking about doing a "SET NOCOUNT OFF". But I have a stored procedure which I use to insert some data into some tables. This procedure creates a xml response string, well let me give you an example:
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int) AS
DECLARE #reply varchar(2048)
... Do a bunch of inserts/updates...
SET #reply = '<xml><big /><outputs /></xml>'
SELECT #reply
GO
So I put together a script which uses this SP a bunch of times, and the xml "output" is getting to be too much (it's crashed my box once already).
Is there a way to suppress or redirect the output generated from this stored procedure? I don't think that modifying this stored procedure is an option.
thanks.
I guess i should clarify. This SP above is being called by a T-SQL Update script that i wrote, to be run through enterprise studio manager, etc.
And it's not the most elegant SQL i've ever written either (some psuedo-sql):
WHILE unprocessedRecordsLeft
BEGIN
SELECT top 1 record from updateTable where Processed = 0
EXEC insertSomeData #param = record_From_UpdateTable
END
So lets say the UpdateTable has some 50k records in it. That SP gets called 50k times, writing 50k xml strings to the output window. It didn't bring the sql server to a stop, just my client app (sql server management studio).
The answer you're looking for is found in a similar SO question by Josh Burke:
-- Assume this table matches the output of your procedure
DECLARE #tmpNewValue TABLE ([Id] int, [Name] varchar(50))
INSERT INTO #tmpNewValue
EXEC [ProcedureB]
-- SELECT [Id], [Name] FROM #tmpNewValue
I think I found a solution.
So what i can do now in my SQL script is something like this (sql-psuedo code):
create table #tmp(xmlReply varchar(2048))
while not_done
begin
select top 1 record from updateTable where processed = 0
insert into #tmp exec insertSomeData #param=record
end
drop table #tmp
Now if there was a even more efficient way to do this. Does SQL Server have something similar to /dev/null? A null table or something?
Answering the question, "How do I suppress stored procedure output?" really depends on what you are trying to accomplish. So I want to contribute what I encountered:
I needed to supress the stored procedure (USP) output because I just wanted the row count (##ROWCOUNT) from the output. What I did, and this may not work for everyone, is since my query was already going to be dynamic sql I added a parameter called #silentExecution to the USP in question. This is a bit parameter which I defaulted to zero (0).
Next if #silentExecution was set to one (1) I would insert the table contents into a temporary table, which is what would supress the output and then execute ##ROWCOUNT with no problem.
USP Example:
CREATE PROCEDURE usp_SilentExecutionProc
#silentExecution bit = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE #strSQL VARCHAR(MAX);
SET #strSQL = '';
SET #strSQL = 'SELECT TOP 10 * ';
IF #silentExecution = 1
SET #strSQL = #strSQL + 'INTO #tmpDevNull ';
SET #strSQL = #strSQL +
'FROM dbo.SomeTable ';
EXEC(#strSQL);
END
GO
Then you can execute the whole thing like so:
EXEC dbo.usp_SilentExecutionProc #silentExecution = 1;
SELECT ##ROWCOUNT;
The purpose behind doing it like this is if you need the USP to be able to return a result set in other uses or cases, but still utilize it for just the rows.
Just wanted to share my solution.
I have recently come across with a similar issue while writing a migration script and since the issue was resolved in a different way, I want to record it.
I have nearly killed my SSMS Client by running a simple while loop for 3000 times and calling a procedure.
DECLARE #counter INT
SET #counter = 10
WHILE #counter > 0
BEGIN
-- call a procedure which returns some resultset
SELECT #counter-- (simulating the effect of stored proc returning some resultset)
SET #counter = #counter - 1
END
The script result was executed using SSMS and default option on query window is set to show “Results to Grid”[Ctrl+d shortcut].
Easy Solution:
Try setting the results to file to avoid the grid to be built and painted on the SSMS client. [CTRL+SHIFT+F keyboard shortcut to set the query results to file].
This issue is related to : stackoverflow query
Man, this is seriously a case of a computer doing what you told it to do instead of what you wanted it to do.
If you don't want it to return results, then don't ask it to return results. Refactor that stored procedure into two:
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int) AS
BEGIN
DECLARE #reply varchar(2048)
--... Do a bunch of inserts/updates...
EXEC SelectOutput
END
GO
CREATE PROCEDURE SelectOutput AS
BEGIN
SET #reply = '<xml><big /><outputs /></xml>'
SELECT #reply
END
From which client are you calling the stored procedure? Say it was from C#, and you're calling it like:
var com = myConnection.CreateCommand();
com.CommandText = "exec insertSomeData 1";
var read = com.ExecuteReader();
This will not yet retrieve the result from the server; you have to call Read() for that:
read.Read();
var myBigString = read[0].ToString();
So if you don't call Read, the XML won't leave the Sql Server. You can even call the procedure with ExecuteNonQuery:
var com = myConnection.CreateCommand();
com.CommandText = "exec insertSomeData 1";
com.ExecuteNonQuery();
Here the client won't even ask for the result of the select.
You could create a SQL CLR stored procedure that execs this. Should be pretty easy.
I don't know if SQL Server has an option to suppress output (I don't think it does), but the SQL Query Analyzer has an option (under results tab) to "Discard Results".
Are you running this through isql?
You said your server is crashing. What is crashing the application that consumes the output of this SQL or SQL Server itself (assuming SQL Server).
If you are using .Net Framework application to call the stored procedure then take a look at SQLCommand.ExecuteNonQuery. This just executes stored procedure with no results returned. If problem is at SQL Server level then you are going to have to do something different (i.e. change the stored procedure).
You can include in the SP a parameter to indicate if you want it to do the select or not, but of course, you need to have access and reprogram the SP.
CREATE PROCEDURE [dbo].[insertSomeData] (#myParam int, #doSelect bit=1) AS
DECLARE #reply varchar(2048)
... Do a bunch of inserts/updates...
SET #reply = '<xml><big /><outputs /></xml>'
if #doSelect = 1
SELECT #reply
GO
ever tried SET NOCOUNT ON; as an option?