I've got a database that is replicated on two servers, a live server and a test server, so that whenever it's needed the 'test' database gets overwritten by the 'live' database (so that I can reset everything if I've made a mess.)
I want an Stored Procedure in the 'test' database, that will only run in the 'test' database, but to do this I need to have it in the 'live' database as well, so that it can be copied over when 'test' is overwritten.
The procedure starts:
if ##SERVERNAME<>'TEST'
begin
raiserror ('NOT ON TEST! This SP must only be run on TEST.',16,1)
return
end
So that if it runs in live, it immediately exits.
Unfortunately the "Live" database server uses an older version of SQL, and doesn't seem to understand the lead/lag/over statements in the script, and refuses to create the procedure because of these "Incorrect syntax" errors.
The SP definitely works in the test server.
Is there a way to disregard the error messages when creating a stored procedure?
I've found a prior question that explained how to make a stored procedure with the same name, but I need the stored procedure to contain the script that the server thinks is incorrect.
The only way to not get the stored-procedure validated when created, is to run a dynamic-sql query within.
Example:
CREATE PROCEDURE dbo.YourStoredProcedure AS
BEGIN
IF ##SERVERNAME<>'TEST'
BEGIN
RAISERROR ('NOT ON TEST! This SP must only be run on TEST.',16,1)
RETURN
END
DECLARE #SQL NVARCHAR = N'
SELECT rowOne
, rowTwo
, valueOne
, LEAD(valueOne) OVER (PARTITION BY rowOne ORDER BY rowTwo DESC) AS preValue
FROM dbo.YourTable
'
EXEC(#SQL)
END
Notes:
On the long term, try to find a better strategy than DB replication for different systems. Check Continuous Deployment
Make sure to check concatenated dynamic-sql for potential issues (sql injection). Check QUOTENAME()
When executing the following in SSMS;
if not exists (select * from sys.databases where name = 'SWFUAT')
begin
print 'The UAT database (SWFUAT) does not exist...'
set noexec on;
end
go
use SWFUAT;
go
The following is displayed;
The UAT database (SWFUAT) does not exist...
Msg 911, Level 16, State 1, Line 20
Database 'SWFUAT' does not exist. Make sure that the name is entered correctly.
Shouldn't the compiler just ignore the "use" statement?
If I understand your question correctly then you want to execute
use SWFUAT;
only when the SWFUAT database is exist.
Unfortunately because of this remark
USE is executed at both compile and execution time and takes effect immediately. Therefore, statements that appear in a batch after the USE statement are executed in the specified database.
described here you can't simply use the SET NOEXEC in your case.
To achieve what you need you should replace your code with for example this
if not exists (select * from sys.databases where name = 'SWFUAT')
begin
print 'The UAT database (SWFUAT) does not exist...'
end else begin
use SWFUAT;
end
go
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"
What is the difference between ; and GO in stored procedure in SQL Server ?
Actually, if I have a stored procedure in SQL server and wanna to put t separate queries inside it which the first one just calculates number of records (count) and the second one selects some records based on some conditions, then what should I use between that two queries?
Go or ;
; just ends the statement.
GO is not a statement but a command to the server to commit the current batch to the Database. It creates a stop inside the transaction.
http://msdn.microsoft.com/en-us/library/ms188037.aspx
(Update, thanks for the comments):
GO is a statement intended for the Management studio as far as I know, maybe to other tools as well.
The semicolon separates queries, the GO command separates batches. (Also GO is not a T-SQL command, it's a command recognised by the sqlcmd and osql utilities and Management Studio.)
You can't use GO inside a stored procedure. If you would try, the definition of the procedure will end there, and the rest will be a separate batch.
A local variable has the scope of the batch, so after a GO command you can't use local variables declared before the GO command:
declare #test int
set #test = 42
GO
select #Test -- causes an error message as #Test is undefined
I know this thread is old but I thought these other uses/differences might be handy for other searches like myself regarding GO.
Anything after the GO will not wind up in your sproc because the GO will execute the CREATE/ALTER PROCEDURE command. For example, if you run this...
CREATE PROCEDURE X AS
SELECT 1 As X
GO
SELECT 2 As X
Then after running it you go back in to edit the procedure you will find that only the SELECT 1 As X is in there because the GO created the sproc and anything after it is assumed to be the next thing you are doing and not part of the sproc.
I'm surprised I haven't seen this mentioned much out there but the batch separator is not only specific to the program you are querying with but in the case of SSMS it is actually user editable! If I went into the settings and changed the batch separator from GO to XX then in my copy of SSMS, XX executes the batch not GO. So what would happen if I tried to execute a stored procedure that contained GO?
Think of GO as a way of telling SSMS to send whatever is above it to the server for execution. The server never receives the GO as that is just there to mark the end of a batch of command you want SSMS to send to the server.
If you have a scenario where you need to control execution flow in your stored procedure then you can use BEGIN TRANSACTION and COMMIT TRANSACTION for that and those are allowed in stored procedures.
GO is not a command to the server, it's the default batch separator for most of the client tools the MS supply. When the client tool encounters "GO" on a new line by itself, it sends whatever commands it has accumulated thus far to the server, and then starts over anew.
What this means is that any variables declared in one batch are not available in subsequent batches. And it also means that multi-line comments can't be placed around a "GO" command - because the server will see the first batch, and see an unterminated comment.
It marks the end of a batch in Query Analyzer and
therefore signals the end of a stored procedure definition in that batch.
As much as i know its not a part of sp.
GO isn't a TSQL command.
And ; just ends the statement.
I am investigating an odd error from a SQL Server 2005 stored procedure which I cannot reproduce by calling it directly from Management Studio.
Therefore I'd like to be able to:
set a breakpoint in the stored procedure
wait for the procedure to be called externally and the breakpoint hit
see the values of the passed-in parameters
step through the stored procedure
Is this possible, and if so how?
You could try the "server explorer" from visual studio but the sqlserver needs to be configured to allow debugging. Here is some info: https://web.archive.org/web/20211020102846/https://www.4guysfromrolla.com/articles/051607-1.aspx. But I think that you first should try Profiler like Eppz say. :)
Use SQL Profiler to view what is happening when the procedure gets called and what params are passed in.
If you can't step through the code, here are two ways:
#1 concatenate a string, and insert into a log file.
Declare a variable like:
DECLARE #Loginfo varchar(7500)
append debug info into it as you progress through the code:
SET #LogInfo=ISNULL(#LogInfo)+'#01> #x='+COALESCE(CONVERT(varchar(10),#x),'NULL')
..
SET #LogInfo=ISNULL(#LogInfo)+'#02>'
..
SET #LogInfo=ISNULL(#LogInfo)+'#03> top loop'
at all exit points (after any rollbacks) add:
INSERT INTO YourLogTable VALUES (... ,#LogInfo )
depending on the transaction usage and you error in particular, you may be able to just insert many times with no fear of rollback, so you will need change this to your situation.
#2 write to a text file on the sq server
this may not be an option because it uses the very insecure xp_cmdshell stored procedure. However, if you can use that and if transactions from the calling app are causing a problem try creating this stored procedure:
CREATE PROC log_message
#Message varchar(255)
,#FileName varchar(100)
,#OverWrite char(1) = 'N'
AS
/*
Log messages to server side text files from stored procedures/triggers/sql scripts
Input parameters:
Message - message to put in the log file
FileName - path and name of the file to log the message into
OverWrite - 'Y'=overwrite entire file with current message
'N'=append current message onto end of file
Return code:
0 - everything was fine
1 - there was an error
NOTE: the command to log the message can not be longer than 255 characters,
as a result the message and file name should be less than 245 chars combined
Example: EXEC log_message 'Duplicates found','C:\logfile.txt', 'N'
append the "Duplicates found" message onto the server's "C:\logfile.txt" file
*/
BEGIN
SET NOCOUNT ON
DECLARE #ExecuteString VARCHAR(255) --command string can only be 255 chars long
DECLARE #ReturnValue int
--build command string
SET #ExecuteString = RTRIM('echo ' + COALESCE(LTRIM(#Message),'-')
+ CASE WHEN (#OverWrite = 'Y') THEN ' > ' ELSE ' >> ' END + RTRIM(#FileName))
--run command string
EXEC #ReturnValue=master..xp_cmdshell #ExecuteString
--IF #ReturnValue!=0
-- PRINT 'command failed, return value='+CONVERT(varchar(40),#ReturnValue)
RETURN #ReturnValue
SET NOCOUNT OFF
END
sprinkle calls to this procedure these through your code write what you need into a file on the server
Update: I am not sure if there is a way to attach to a running stored proc. You can use profiler to get a real time trace of the statements getting executed (SP:StmtStarting). Also check out Apex SQL Debug which seems to have more capabilities and is available as an Add-in to Management Studio.
If you have Visual Studio, it is easy to debug:
Debugging Stored Procedures in Visual Studio 2005
More answers here:
What’s your favored method for debugging MS SQL stored procedures?
I would use a combination of SQL Profiler and print statements inside of your SQL statement. Not sure of a way to step thru line by line but using profiler in combination with print and select statements (if using temp tables) to view their contents as the proc runs will quickly shed light on what's happening.