Execute multiple statements inside Stored Procedure - sql

I am trying to execute multiple statements inside a stored procedure. It's basically a serverless architecture where I cannot generate SQL scripts and run those to generate SPs or tables. The only way I have is to run the scripts via SP or Functions. Is there any clue how can I execute bulk statements programmatically. I tried with the following SP:
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
Alter PROCEDURE [dbo].[GenerateScript]
AS
BEGIN
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Bar1]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'
CREATE PROCEDURE [dbo].[Bar1]
AS
BEGIN
Select Foo from Table
END
'
END
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Bar2]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'
CREATE PROCEDURE [dbo].[Bar2]
AS
BEGIN
Select Foo from Table
END
'
END
END
This is producing the error below:
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.

The problem isn't your sp_executesql commands, it's the start of your SQL (The error is literally telling you the problem). As the error tells you CREATE/ALTER PROCEDURE' must be the first statement in a query batch, however, the first command you have in your batch is SET ANSI_NULLS ON.
Add a GO between your SET and ALTER commands to start a new batch (in SSMS/sqlcmd):
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GenerateScript] ...

Related

Creating a Function if doesn't exist and alter the function if it already exists

I am working on creating a scalar valued function but before creating it I would like to check if it exists and if it doesn't exist then create the function using dynamic script and then alter it normally. I got this working for a stored procedure but I couldn't do the same with a function.
my procedure is as follows
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[region]') AND OBJECTPROPERTY(id,N'IsProcedure') = 1)
BEGIN
EXEC dbo.sp_executesql #statement = N' CREATE PROCEDURE [dbo].[region] AS BEGIN Print ''A'' END'
END
GO
ALTER PROCEDURE [dbo].[region](---)
AS
---
END
I tried to follow the same approach for a scalar valued function as follows
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[region]') AND OBJECTPROPERTY(id,N'IsScalarFunction') = 1)
BEGIN
EXEC dbo.sp_executesql #statement = N' CREATE FUNCTION [dbo].[region] AS RETURN 0'
GO
ALTER FUNCTION dbo.region(#dd datetime)
--
GO
But, above script threw me an error Incorrect syntax near the keyword 'AS'.
You need to specify parameters (even if none) and a return type for the function
EXEC dbo.sp_executesql #statement = N' CREATE FUNCTION [dbo].[region]() RETURNS VARCHAR AS BEGIN RETURN ''A'' END'

SQL Server object version control

We have got database deployment process which applies to production database. This process will change the definition of the store procedures/views/functions if they are not up to date with the current deployment. Problem is even though they are not changed deployment process will run ALTER step to make sure they are up to date. As we have got OLTP environment, during the deployment we get blocking on the stored procedures/functions.
What I want to implement is only alter the SP/view/function if the definition is changed.
We have already considered doing HashValue Compare of object definition (Does not work with large definition), date modified compare for object from system tables, SSDT, Redgate SQL Compare/Source Control.
Is there any other method which we can use to compare the definition during deployment and only apply the alter script if it is changed.
Thanks in advance for your input it this matter.
You can leverage Schema Compare functionality of SSDT. Also when you publish database project in SSDT, it will generate a script only containing the difference.
Please take a look at an introduction video at http://channel9.msdn.com/Events/Visual-Studio/Launch-2013/VS108
You can try using the following script.
basically it creates a Stored Procedure that gets as a parameter an object name (SP/view/function) and an alter command for it. The stored procedure checks if the 'Alter' argument will change the existing object, and only if it will it will run it.
/****** Object: StoredProcedure [dbo].[AlterDbObjectIfNecessary] Script Date: 05/14/2014 09:28:30 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[AlterDbObjectIfNecessary]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[AlterDbObjectIfNecessary]
GO
/****** Object: StoredProcedure [dbo].[AlterDbObjectIfNecessary] Script Date: 05/14/2014 09:28:30 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- create new user-toolset relation/ toolset User
CREATE PROCEDURE [dbo].[AlterDbObjectIfNecessary]
#alterCommand NVARCHAR(MAX) ,
#AlteredObjectname NVARCHAR(MAX)
AS
BEGIN
DECLARE #objId INT
DECLARE #objName NVARCHAR(max)
DECLARE #Def nvarchar(max)
--Getting the ID of the object you want to alter
SELECT
#objId = id, #objName = o.name
FROM sysobjects o
WHERE o.type IN ('P', 'TR', 'V', 'TF', 'FN', 'IF')
AND o.name = #AlteredObjectname
--Getting the later definittion
SELECT #Def = [definition]
FROM sys.sql_modules
WHERE object_id = #objId
--print #objName
--print #def
SET #def = REPLACE(#def, 'create PROC','alter PROC')
SET #def = REPLACE(#def, 'create procedure','alter procedure')
SET #def = REPLACE(#def, 'create function','alter function')
SET #def = REPLACE(#def, 'create view','alter view')
IF UPPER (#def) <> UPPER (#alterCommand)
BEGIN
BEGIN TRY
--PRINT #alterCommand
--Alter the object to new definition
EXEC sp_executesql #alterCommand
PRINT #objName + ' Was Modified'
END TRY
BEGIN CATCH
PRINT 'Error. Failed to modify ' + #objName + ' : ' + CONVERT(nvarchar(10), ERROR_NUMBER()) + ' ' + ERROR_MESSAGE()
END CATCH
END
ELSE
BEGIN
PRINT 'Modification was not required for ' +#objName
END
END
GO
You can using it in the following manner:
DECLARE #alterCommand NVARCHAR(MAX)
DECLARE #AlteredObjectname NVARCHAR(MAX)
SET #AlteredObjectname = 'MyObjectName' -- <Insert here the name of object you want to alter
SET #alterCommand = 'ALTER PROCEDURE [dbo].[MyObjectName]
-- The rest of the alter stattement
'
EXEC AlterDbObjectIfNecessary #AlteredObjectname, #alterCommand

Creating a stored procedure if it does not already exist

I want to check if a list of stored procedures exist. I want this all to be done in 1 script, one by one. So far I have this format:
USE [myDatabase]
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'sp_1')
BEGIN
CREATE PROCEDURE sp_1
AS
.................
END
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'sp_2')
BEGIN
CREATE PROCEDURE sp_2
AS
.................
END
GO
and so on. However, I'm getting the following error:
Incorrect syntax near the keyword 'Procedure'.
Why isn't what I'm doing working correctly?
CREATE PROCEDURE must be the first statement in the batch. I usually do something like this:
IF EXISTS (
SELECT type_desc, type
FROM sys.procedures WITH(NOLOCK)
WHERE NAME = 'myProc'
AND type = 'P'
)
DROP PROCEDURE dbo.myProc
GO
CREATE PROC dbo.myProc
AS
....
GO
GRANT EXECUTE ON dbo.myProc TO MyUser
(don't forget grant statements since they'll be lost if you recreate your proc)
One other thing to consider when you are deploying stored procedures is that a drop can succeed and a create fail. I always write my SQL scripts with a rollback in the event of a problem. Just make sure you don't accidentally delete the commit/rollback code at the end, otherwise your DBA might crane-kick you in the trachea :)
BEGIN TRAN
IF EXISTS (
SELECT type_desc, type
FROM sys.procedures WITH(NOLOCK)
WHERE NAME = 'myProc'
AND type = 'P'
)
DROP PROCEDURE myProc GO
CREATE PROCEDURE myProc
AS
--proc logic here
GO
-- BEGIN DO NOT REMOVE THIS CODE (it commits or rolls back the stored procedure drop)
IF EXISTS(
SELECT 1
FROM sys.procedures WITH(NOLOCK)
WHERE NAME = 'myProc'
AND type = 'P'
)
COMMIT TRAN
ELSE
ROLLBACK TRAN
-- END DO NOT REMOVE THIS CODE
One idiom that I've been using lately that I like quite a lot is:
if exists (select 1 from sys.objects where object_id = object_id('dbo.yourProc'))
set noexec on
go
create procedure dbo.yourProc as
begin
select 1 as [not yet implemented]
end
go
set noexec off
alter procedure dbo.yourProc as
begin
/*body of procedure here*/
end
Essentially, you're creating a stub if the procedure doesn't exist and then altering either the stub (if it was just created) or the pre-existing procedure. The nice thing about this is that you don't drop a pre-existing procedure which drops all the permissions as well. You can also cause issues with any application that happens to want it in that brief instant where it doesn't exist.
[Edit 2018-02-09] - In SQL 2016 SP1, create procedure and drop procedure got some syntactic sugar that helps with this kind of thing. Specifically, you can now do this:
create or alter dbo.yourProc as
go
drop procedure if exists dbo.yourProc;
Both provide idempotency in the intended statement (i.e. you can run it multiple times and the desired state is achieved). This is how I'd do it now (assuming you're on a version of SQL Server that supports it).
I know that there's an accepted answer, but the answer does not address exactly what the original question asks, which is to CREATE the procedure if it does not exist. The following always works and has the benefit of not requiring dropping procedures which can be problematic if one is using sql authentication.
USE [MyDataBase]
GO
IF OBJECT_ID('mySchema.myProc') IS NULL
EXEC('CREATE PROCEDURE mySchema.myProc AS SET NOCOUNT ON;')
GO
ALTER PROCEDURE mySchema.myProc
#DeclaredParmsGoHere DataType
AS
BEGIN
DECLARE #AnyVariablesINeed Their DataType
SELECT myColumn FROM myTable WHERE myIndex = #IndexParm
Updated on Sep 2020
You can use CREATE OR ALTER statement (was added in SQL Server 2016 SP1):
The CREATE OR ALTER statement acts like a normal CREATE statement by creating the database object if the database object does not exist and works like a normal ALTER statement if the database object already exists.
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spGetRailItems]') AND type in (N'P', N'PC'))
BEGIN
execute ('
CREATE PROCEDURE [dbo].[spGetRailItems]
AS
BEGIN
Declare #isLiftedBagsEnable bit=1;
select #isLiftedBagsEnable=cast(DataValu as bit) from setups where scope =''Rail Setting'' and dataName = ''isLiftedBagsEnable'';
IF #isLiftedBagsEnable=1
BEGIN
IF EXISTS (SELECT * FROM ITEMCONFIG)
BEGIN
SELECT [Item],[Desc] FROM ProcData WHERE Item IN (SELECT Item FROM ItemConfig) ORDER BY [Desc]
END
ELSE
BEGIN
SELECT [Item],[Desc] FROM ProcData ORDER BY [Desc]
END
END
ELSE
BEGIN
SELECT [Item],[Desc] FROM ProcData ORDER BY [Desc]
END
END
')
END
exec spGetRailItems;
Just in case if you are using SQL server 2016, then there is a shorter version to check if the proc exist and then drop and recreate it
USE [DATABASENAME]
GO
DROP PROCEDURE IF EXISTS <proc name>
GO
CREATE PROCEDURE <proc name>
AS
-- your script here
END
GO
GRANT EXECUTE ON <proc name> TO <username>
Source : https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/
USE [myDatabase]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'sp_1')
BEGIN
DROP PROCEDURE sp_1
END
GO --<-- Add a Batch Separator here
CREATE PROCEDURE sp_1
AS
.................
END
GO
In SQL Server 2017 and later versions you can use the "IF EXISTS" to drop a proc or even better you can use "CREATE OR ALTER PROCEDURE"
USE [myDatabase]
GO
DROP PROCEDURE IF EXISTS sp_1;
GO --<-- Add a Batch Separator here
CREATE OR ALTER PROCEDURE sp_1
AS
BEGIN
.................
END
GO
You can simply ignore the "DROP IF EXISTS" command and just use "CREATE OR ALTER"
I like to use ALTER so I don't lose permissions and if you have a syntax error the old version still exists:
BEGIN TRY
--if procedure does not exist, create a simple version that the ALTER will replace. if it does exist, the BEGIN CATCH will eliminate any error message or batch stoppage
EXEC ('CREATE PROCEDURE AAAAAAAA AS DECLARE #A varchar(100); SET #A=ISNULL(OBJECT_NAME(##PROCID), ''unknown'')+'' was not created!''; RAISERROR(#A,16,1);return 9999')
END TRY BEGIN CATCH END CATCH
GO
ALTER PROCEDURE AAAAAAAA
(
#ParamsHere varchar(10)
)
AS
PRINT 'HERE IN '+(OBJECT_NAME(##PROCID))
GO
you can execute the following:
DROP PROCEDURE IF EXISTS name_of_procedure;
CREATE PROCEDURE name_of_procedure(....)

Error trying to execute SQL Server script file via ExecuteNonQuery on VB.NET

The script works great on SQL Management Studio, it just doesn't on the program. The script file checks if certain stored procedures exist, drops them if they do, and next declares them again, like this:
IF EXISTS(SELECT * FROM sys.objects WHERE type='P' AND name = 'myProc1')
DROP PROCEDURE myProc1
GO
CREATE PROCEDURE myProc1
AS
BEGIN
SELECT
[Field1] = ((*Some numeric value*)-(*Some numeric value*)),
[Field2] = ((*Some numeric value*)-(*Some numeric value*)),
[Field3] = ((*Some numeric value*)-(*Some numeric value*))
FROM ...
WHERE ...
END
GO
IF EXISTS(SELECT * FROM sys.objects WHERE type='P' AND name = 'myProc2')
DROP PROCEDURE myProc2
GO
CREATE PROCEDURE myProc2
AS
BEGIN
SELECT * FROM Table2
END
GO
IF EXISTS(SELECT * FROM sys.objects WHERE type='P' AND name = 'myProc3')
DROP PROCEDURE myProc3
GO
CREATE PROCEDURE myProc3
AS
BEGIN
SELECT * FROM Table3
END
GO
And when I try to run it on VB.NET I tried this:
sql.CommandText = File.OpenText("C:\test.sql").ReadToEnd()
sql.CommandType = CommandType.Text
sql.Connection = connects.con
sql.ExecuteNonQuery()
And I get this error:
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Incorrect syntax near 'GO'.
Incorrect syntax near ')'.
Incorrect syntax near '-'.
Incorrect syntax near ')'.
Incorrect syntax near '-'.
Incorrect syntax near ')'.
Incorrect syntax near '-'.
Incorrect syntax near '�'.
So at least 3 GOs, the 3 operations, and even some unknown character.
Any ideas on what I'm doing wrong?
I know this is an old one but I do this alot and to get around it I generate scripte for the stored procedures form SQL Management studio and under advanced I tell it to "check for object existence" SQL 2010+ (pre-2010 was check object exist or something simular) and also set "Script DROP and CREATE" to "Script DROP and CREATE".
Then just take out the "GO"'s (I usually replace-all GO with blank with match case on or go through one by one clicking replace so I know it's the correct GO's being replaced) from the script and it'll run perfect in VB.NET/t-SQL.
This means it generates a script like this:
/****** Object: StoredProcedure [dbo].[test3] Script Date: 11/03/2015 16:47:48 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test3]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[test3]
/****** Object: StoredProcedure [dbo].[test2] Script Date: 11/03/2015 16:47:48 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test2]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[test2]
/****** Object: StoredProcedure [dbo].[test1] Script Date: 11/03/2015 16:47:48 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test1]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[test1]
/****** Object: StoredProcedure [dbo].[test1] Script Date: 11/03/2015 16:47:48 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test1]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[test1]
AS
BEGIN
print ''dummy procedure 1''
END
'
END
/****** Object: StoredProcedure [dbo].[test2] Script Date: 11/03/2015 16:47:48 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test2]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[test2]
AS
BEGIN
print ''dummy procedure 2''
END
'
END
/****** Object: StoredProcedure [dbo].[test3] Script Date: 11/03/2015 16:47:48 ******/
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[test3]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql #statement = N'-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE PROCEDURE [dbo].[test3]
AS
BEGIN
print ''dummy procedure 3''
END
'
END
I started doing this around 5 years ago and never had any issues
Hope this helps people
Regards
Liam
The GO command is a Batch separator for SMSS and is not part of T-SQL command. Your script then will work in SSMS because it will execute each batch. On the other hand the .Net Framework pass the script to the underlying SQL engine and consider it as ONE big script not separate batches which should be and there therefore lies your error.
So creating three Stored Procedures in one Command would not work.
My suggestion is you create the script on your back-end separately and then your test.sql should look like this:
EXEC myProc1
EXEC myProc2
EXEC myProc3
However, if you want to create three stored procedure on the fly then you probably just separate each CREATE PROCEDURE into three different sql text file, namely, test1.sql, test2.sql and test3.sql for myProc1, myProc2 and myProc3 respectively.

Procedure for dropping unique constraint

I am trying to write a procedure to drop the Unique constraints from any table quicker.
IF EXISTS
(SELECT *
FROM dbo.sysobjects
WHERE id = object_id(N'[dba].[spu_drop_uq_index]'))
DROP PROCEDURE [dba].[spu_drop_uq_index]
GO
CREATE PROCEDURE [dba].[spu_drop_uq_index] (#table varchar(1000), #index varchar(1000))
AS
BEGIN
DECLARE #sql varchar(1000)
SET #sql = 'ALTER TABLE ['+#table+'] DROP CONSTRAINT ['+#index+']'
IF EXISTS (SELECT name FROM sysindexes WHERE name = #index)
EXEC #sql
END
GO
EXEC [dba].[spu_drop_uq_index] #table = 'aaa', #index = 'UQ_xxx'
GO
But I get an error:
The name 'ALTER TABLE [aaa] DROP CONSTRAINT [UQ_xxx]' is not a valid identifier.
However, if I execute this not dynamically, it succeeds:
ALTER TABLE [aaa] DROP CONSTRAINT [UQ_xxx]
What am I doing wrong? :) Thanks!
Use
exec sp_executesql #sql
instead of EXEC, or put the #sql in parenthesis
Exec (#sql)
sp_executesql is preferred: http://msdn.microsoft.com/en-us/library/ms175170(v=sql.105).aspx
To execute a string, we recommend that you use the sp_executesql stored procedure instead of the EXECUTE statement. Because this stored procedure supports parameter substitution, sp_executesql is more versatile than EXECUTE; and because sp_executesql generates execution plans that are more likely to be reused by SQL Server, sp_executesql is more efficient than EXECUTE.
Wrap the exec string in brackets:
EXEC (#sql)
When executing dynamic strings, brackets are required. When executing sprocs, they're not.