Stored procedure with a GO statement to free up resources - sql

I have a stored procedure in a SQL Server 2019 database that does various checks for me when I run. This stored procedure contains several SELECT type statements - if the condition is true it will send an email.
This all works great when I run the whole stored procedure. As none of the statements are linked, I thought it would be more efficient if I ran each statement then had a GO command to release any memory or resources from previous SELECT.
But I can't do this as the GO does not work when I run me ALTER script on the stored procedure. It cuts off any lines after the GO. Is there any alternative T-SQL command that I can use similar to GO to free up resources?
ALTER PROCEDURE [dbo].[uspDataChecker]
AS
SET NOCOUNT OFF;
IF ((SELECT COUNT(DISTINCT GroupName) FROM tblSurveys) <> 5)
BEGIN
EXEC uspWebmasterSendEmail 'FAILED GroupName Check', 'GroupName does not contain 5 rows as expected'
END
-- I would like to put a "GO" statement here but when I
-- execute my ALTER script it chops off the below check
IF ((SELECT COUNT(*) FROM tblAccounts) <> 0)
BEGIN
EXEC uspWebmasterSendEmail 'FAILED Accounts Check', 'tblAccounts has rwos'
END

Related

Conditionally create stored procedure using TSQL [duplicate]

This question already has answers here:
How do I conditionally create a stored procedure in SQL Server?
(11 answers)
Closed 7 years ago.
I'm writing a db update script which basically retrieve the current version number from the database then create a number of stored procedure if the version is valid. If the current version does not match the expected version then it should skip executing the code.
However I run into a problem when I write the script because CREATE PROCEDURE has to be the first statement in a batch, so it's not possible for me to insert if .. else statement before the create procedure statement.
I've also tried using GOTO but to no avail because GOTO doesn't span across multiple batches. Same thing applies to RETURN and RAISEERROR - the rest of the code will still execute.
Sample script:
IF #Version = '1.0' --doesn't work
BEGIN
CREATE PROCEDURE dbo.uspCreateAccount
AS BEGIN
--The rest of the code goes here
END
END
Can anyone provide some insight on this?
You can accomplish this using the exec functionality.
IF #Version = '1.0'
BEGIN
--Drop it if it already exists
IF OBJECT_ID('uspCreateAccount', 'P') IS NOT NULL
EXEC ('DROP PROCEDURE uspCreateAccount')
--Recreate it.
EXEC('
CREATE PROCEDURE uspCreateAccount
AS BEGIN
--The rest of the code goes here
END
')
END
You can use exec to run a SQL command in a new scope. The following snippet will run even when dbo.YourProc already exists, because the SQL command inside exec() will never be parsed in that case.
if not exists (select * from sys.procedures where name = 'YourProc')
exec ('create procedure dbo.YourProc as select 1 as a')
go
alter procedure dbo.YourProc
as
select ...
This construct creates an empty stub procedure if the procedure does not exist. If the procedure exists, it runs alter. So it preserves rights that have been granted on the procedure.

How to execute a stored procedure after it is created?

I'm trying to execute a stored procedure directly after its creation however it is not getting called. It looks like the stored procedure is not yet created during the execution call.
Here is how the script looks like:
CREATE PROCEDURE sp_Transfer_RegionData
AS
BEGIN
INSERT INTO Region (regionName)
SELECT column1
FROM openquery(ITDB, 'select * from db.table1')
END
EXEC sp_Transfer_RegionData
The script runs fine however the needed table is not populated. After replacing the execution part with:
IF OBJECT_ID('sp_Transfer_RegionData') IS NOT NULL
begin
exec [dbo].[sp_Transfer_RegionData]
print 'tada'
end
I could see that the stored procedure does not exist when it has to be executed. Couldn't find a solution for this in the internet...
So how to make the SQL script run sync so that the stored procedure would already exist during the execution part?
You need a GO after you created the SP otherwise you have created a recursive SP that calls itself "indefinitely" which is 32 times in SQL Server.
Maximum stored procedure, function, trigger, or view nesting level
exceeded (limit 32).
Try this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE sp_Transfer_RegionData
AS
BEGIN
INSERT INTO Region (regionName)
SELECT column1
FROM openquery(ITDB, 'select * from db.table1')
END
GO
EXEC sp_Transfer_RegionData

T-SQL install / upgrade script as a transaction

I'm trying to write a single T-SQL script which will upgrade a system which is currently in deployment. The script will contain a mixture of:
New tables
New columns on existing tables
New functions
New stored procedures
Changes to stored procedures
New views
etc.
As it's a reasonably large upgrade I want the script to rollback if a single part of it fails. I have an outline of my attempted code below:
DECLARE #upgrade NVARCHAR(32);
SELECT #upgrade = 'my upgrade';
BEGIN TRANSACTION #upgrade
BEGIN
PRINT 'Starting';
BEGIN TRY
CREATE TABLE x ( --blah...
);
ALTER TABLE y --blah...
);
CREATE PROCEDURE z AS BEGIN ( --blah...
END
GO --> this is causing trouble!
CREATE FUNCTION a ( --blah...
END TRY
BEGIN CATCH
PRINT 'Error with transaction. Code: ' + ##ERROR + '; Message: ' + ERROR_MESSAGE();
ROLLBACK TRANSACTION #upgrade;
PRINT 'Rollback complete';
RETURN;
END TRY
END
PRINT 'Upgrade successful';
COMMIT TRANSACTION #upgrade;
GO
Note - I know some of the syntax is not perfect - I'm having to re-key the code
It seems as though I can't put Stored Procedures into a transaction block. Is there a reason for this? Is it because of the use of the word GO? If so, how can I put SPs into a transaction block? What are the limitations as to what can go into a transaction block? Or, what would be a better alternative to what I'm trying to achieve?
Thanks
As Thomas Haratyk said in his answer, your issue was the "go". However, you can have as many batches in a transaction as you want. It's the try/catch that doesn't like this. Here's a simple proof-of-concept:
begin tran
go
select 1
go
select 2
go
rollback
begin try
select 1
go
select 2
go
end try
begin catch
select 1
end catch
Remove the GO and create your procedure by using dynamic sql or it will fail.
EXEC ('create procedure z
as
begin
print "hello world"
end')
GO is not a SQL keyword, it is a batch separator. So it cannot be included into a transaction.
Please refer to those topics for further information :
sql error:'CREATE/ALTER PROCEDURE' must be the first statement in a query batch?
Using "GO" within a transaction
http://msdn.microsoft.com/en-us/library/ms188037.aspx

SQL Unit Testing with TSQLUNIT

I noticed something weird while unit testing an update/insert stored procedure using TSQLUNIT on SQL Server 2012. when I call Exec tsu_RunTests, my test procedure runs but with unexpected behaviour. the line in the code that calls my original stored procedure is executed but no actual updates or inserts made to the database table as expected. Is there a valid reason for this behaviour? Or is this a bug I need to pay much attention to? I notice that when I execute the same original stored procedure outside of the test procedure, it works fine.
You can use Default parameter for each stored procedure and set this #IsTest parameter to true when use these stored procedures in tsu_RunTests.
CREATE PROCEDURE orginal_proc
--#parameter_name
#IsTest BIT = 0
AS
if #IsTest <> 1 Begin
-- Test statements
End Else Begin
-- statements
End
GO
you also can use ##NESTLEVEL for check that your procedure execute directly or execute by other procedure.
CREATE PROCEDURE orginal_proc
--#parameter_name
AS
if ##NESTLEVEL <> 1 Begin
-- Test statements
End Else Begin
-- statements
End
GO
EDIT : Stored Procedure Code must be like below :
If ##NESTLEVEL <> 1 Print 'Befor Update Message'
If ##NESTLEVEL = 1 Begin
Update YourTable
Set ...
End
If ##NESTLEVEL <> 1 Print 'After Update Message'

SQL Create Procedure Abort Logic

Good afternoon all -
I have a temporary stored procedure that needs to be run as a hotfix in several places and I'd like to abort the creation and compilation of the SP if the version of the application is not exactly what I enter. I have the basic idea working but I'd like the messages to come out without all the schema issues from trying to compile the SP.
Here is basically what I have:
IF EXISTS ... DROP PROCEDURE
SELECT TOP 1 Version INTO #CurrentVersion FROM Application_Version ORDER BY UpdateDate DESC
IF NOT EXISTS (SELECT 1 FROM #CurrentVersion WHERE Version = 10)
RAISERROR ('This is for U10 only. Check the application version.', 20, 1) WITH LOG
CREATE PROCEDURE ....
The RAISERROR causes the SP to not end up in the DB and I do get an error but I also end up with schema errors due to schema changes in the past. Due to the SP needing to be the first statement in the batch I can't use IF / ELSE and NOEXEC yields the same results as RAISERROR (without the error).
Any ideas for what can be done to get all of the same results as above without the SP checking the schema if it hits the RAISERROR so I don't end up with a bunch of extra messages reported?
What you want is the error condition to stop execution of the script, which is possible in SQLCMD mode of the query editor with a simple :on error exit:
:on error exit
SELECT TOP 1 Version INTO #CurrentVersion FROM Application_Version ORDER BY UpdateDate DESC
IF NOT EXISTS (SELECT 1 FROM #CurrentVersion WHERE Version = 10)
RAISERROR ('This is for U10 only. Check the application version.', 16, 1);
go
IF EXISTS ... DROP PROCEDURE
go
CREATE PROCEDURE ....
...
go
With this in place there is no need to raise severity 20. Severity 16 is enough, which will take care of the ERRORLOG issue you complain.
The RETURN statement will exit out of a SP. When doing error checking, put a BEGIN and END after your IF statement and after your RAISERROR put a RETURN statement.
There are a couple of options here. My approach would be as follows, because I feel that it provides the best flow:
IF EXISTS ... DROP PROCEDURE
IF EXISTS (SELECT * FROM Application_Version WHERE Version = 10)
BEGIN
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'CREATE PROCEDURE blablabla AS
BEGIN
-- Your Procedure HERE
END'
EXEC sp_executesql #sql
END ELSE
RAISERROR ('This is for U10 only. Check the application version.', 20, 1) WITH LOG