create stored procedure if doesn't exist in sql server - sql

Oracle does "create or replace" statements. Sql server does not seem to - if you are scripting out from Enterprise Manager, it instead suggests "drop and create" instead. Drop and create is undesirable in any situation where you've done grants on the stored procedure, because it tosses out any grants your database administration team has done. You really need "create or replace" to help with separation of conerns between developers and administrators.
What I've been doing recently is this:
use [myDatabase]
go
create procedure myProcedure as
begin
print 'placeholder'
end
go
alter procedure myProcedure as
begin
-- real sproc code here
end
go
This does what I want. If the procedure doesn't exist, create it then alter in the correct code. If the procedure does exist, the create fails and the alter updates the code with the new code.
It creates a different problem for the administrators, because the create throws a misleading error if the stored procedure already exists. Misleading, of course, in the fact that you shouldn't see red error text when the desired outcome has occured.
Does anyone have a way to suppress the red text? Everything I've tried leads to a 'CREATE/ALTER PROCEDURE must be the first statement in a query batch' error in some way or another.

This will work and keep the permissions intact:
use [myDatabase]
go
if object_id('dbo.myProcedure', 'p') is null
exec ('create procedure myProcedure as select 1')
go
alter procedure myProcedure as
SET NOCOUNT ON
-- real sproc code here. you don't really need BEGIN-END
go

Like this:
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[myProcedure]')
AND type in (N'P', N'PC'))
BEGIN
EXEC('
create procedure myProcedure as
begin
print ''placeholder''
end
')
END
EXEC('
alter procedure myProcedure as
begin
-- real sproc code here
end
')
NOTES:
remember to double up your quotes in the dynamic SQL strings.
I have indented it for readability, but that will also add the extra indent spaces to your actual procedures listings. If you don't wnat that, then just reduce the indentation level on the dynamic SQL text.

Finally the day is here where SQL Server has implemented an equivalent to Create or Replace. Their equivalent is "Create or Alter". This is available as of SQL Server 2016 SP1. Example usage:
use [myDatabase]
go
Create or Alter procedure myProcedure as
begin
-- procedure code here
end
go

Related

Microsoft SQL server: copy table from linked server to current database using a stored procedure

I am using Microsoft SQL server. the following code works if run from a QUERY:
SELECT *
INTO mydatabase.dbo.atable
FROM linkedserver.sandbox.dbo.atable
but it does not if inserted into a stored procedure:
SET ANSI_NULLS ON GO
SET QUOTED_IDENTIFIER ON GO
ALTER PROCEDURE dataMigration
AS BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT *
INTO mydatabase.dbo.atable
FROM linkedserver.sandbox.dbo.atable
END
GO
Command(s) completes successfully but no table is created into mydatabase. Sorry for the trivial question. I had a look at similar issues but i did not find a case similar to mine.
Thank you for your help.
You have to execute the stored Procedure after you run the code to alter it.
try running:
exec dataMigration
Right clickOption Image the store procedure and click "Execute Store procedure"

Update Stored procedure (T-SQL ) modification automatically?

I have two identical stored procedures with dynamic query.
Let's say
Stored procedure A
Stored procedure B
Both are in different databases. But they have the same code (Not Complete Identical.4-5 lines differ).
Is there a way to update any modification done in stored procedure A to stored procedure B automatically?
Otherwise I always need to copy and paste changes manually. It is an error-prone activity. Can anyone help me on this ?
You could do something like that:
In database A:
design your stored procedure in a way, that you have a parameter for
the database in which you want to do the work
In database B:
Create a synonym for the procedure in database A
Example:
--create procedure in database A
create procedure dbo.StoredProc
(
#dbname --or dbid if you want
)
as
begin
--create your sql command here, using dynamic sql maybe
declare #sqlcmd NVARCHAR(MAX)=N''
set #sqlcmd = 'SELECT * FROM ' + #dbname + '.dbo.AnyTable'
exec sp_executesql #sqlcmd
end
--create a synonym for this procedure in database b:
create synonym dbo.StoredProc FOR databaseA.dbo.StoredProc
--then you can call your procedure in Database A and B like this:
declare #dbname NVARCHAR(100) = DB_NAME()
exec dbo.StoredProc #dbname
so you have to maintain your code only once, and in database b you only have kind of a "link" to this procedure.
hope this helps :)
This is basically what SSDT was designed for, the idea is that you write your T-SQL and schema as CREATE statements, you build a "dacpac" and then you use sqlpackage.exe to deploy the dacpac to whatever database you want.
Doing it this way you have an overhead of the SSDT project but it fixes exactly your main problem with the existing method "It is an error-prone activity."
My blog post shows how to get an existing database into SSDT (in this case adventureworks but replace adventureworks with your database):
https://the.agilesql.club/Blog/Ed-Elliott/AdventureWorksCI-Step2-MDF-To-Dot-Sql
Ed

Why is this statement included in the stored procedure definition?

I got nailed today by a typo, its a simple typo when I defined a script to create a stored procedure under Sql Server 2005, I forgot a GO statement in between the END statement and the GRANT statement, this lead to the GRANT statement being included in the stored procedure definition and running at the end of the stored procedure (verified with SQL Profiler).
Here is the code:
USE [TestGround]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[FooProc]
AS
BEGIN
SELECT * FROM dbo.Foo
END
-- a GO statement is missing here..
GRANT EXECUTE ON [dbo].[Foo] TO dbo
I understand that GO signifies the end of a batch, but I was surprised to see that the GRANT statement was included in the stored procedure, I've never forgotten the GO statement before so haven't seen this issue.
Could someone please explain to me why this happens?
You can leave out the BEGIN / END pair: the definition doesn't end at END. It continues until the end of the file or a GO, whichever comes first.

Should we end stored procedures with the GO statement?

Should we end stored procedures with GO statement, if so what are the advantages of using GO?
CREATE PROCEDURE uspGetAddress #City nvarchar(30)
AS
SELECT *
FROM AdventureWorks.Person.Address
WHERE City = #City
GO
The statement go, per the documentation
Signals the end of a batch of Transact-SQL statements to the SQL Server utilities.
...
GO is not a Transact-SQL statement; it is a command recognized by the sqlcmd and osql
utilities and SQL Server Management Studio Code editor.
SQL Server utilities interpret GO as a signal that they should send the current batch
of Transact-SQL statements to an instance of SQL Server. The current batch of statements
is composed of all statements entered since the last GO, or since the start of the
ad-hoc session or script if this is the first GO.
A Transact-SQL statement cannot occupy the same line as a GO command. However, the line
can contain comments.
Users must follow the rules for batches. For example, any execution of a stored procedure
after the first statement in a batch must include the EXECUTE keyword. The scope of
local (user-defined) variables is limited to a batch, and cannot be referenced after a
GO command.
A stored procedure definition, per the documentation for create procedure, comes with restrictions. it must be the first (and only) statement in the batch:
The CREATE PROCEDURE statement cannot be combined with other Transact-SQL statements in
a single batch.
That means the body of stored procedure ends with the batch. Adding GO in your source file is good practice. Especially since it's common to do things prior to and following the creation of a stored procedure. You'll often see source files that look something like this:
if (object_id('dbo.foobar') is not null ) drop procedure dbo.foobar
GO
-- dbo.foobar --------------------------------------------
--
-- This stored procedure does amazing and wonderful things
----------------------------------------------------------
create procedure dbo.foobar
as
...
{a sequence of amazing and wonderful SQL statements}
...
return 0
GO
grant execute on dbo.foobar to some_schema
GO
And the value for GO is adjustable in Sql Server Management Studio's options. If you'd like to use something like jump instead of go, you can (bearing in mind that you're almost certainly going to give yourself grief in doing so.).
No, you should end your procedure with RETURN.
CREATE PROCEDURE uspGetAddress #City nvarchar(30)
AS
SELECT *
FROM AdventureWorks.Person.Address
WHERE City = #City
RETURN
The GO is really meant to separate commands in a sql script.
Just wanted to point out that without a GO at the end of your stored procedure, any T-SQL after the supposed end of the procedure body will still be included in the body of the proc.
For example
CREATE PROCEDURE Foo
BEGIN
SELECT * FROM dbo.Bar;
END
DROP TABLE dbo.Bar;
In this example, running EXEC dbo.Foo will end up dropping the table even though it is after the END. To avoid that, you need to place a GO after the END.
I prefer to surround the body of the stored procedure with begin and end statements:
CREATE PROCEDURE uspGetAddress (
#City nvarchar(30)
) AS
BEGIN
SELECT *
FROM AdventureWorks.Person.Address
WHERE City = #City;
END;
GO is a not a T-SQL command. It is understood by the tools that run scripts. As the documentation describes:
GO is not a Transact-SQL statement; it is a command recognized by the
sqlcmd and osql utilities and SQL Server Management Studio Code
editor.
SQL Server utilities interpret GO as a signal that they should send
the current batch of Transact-SQL statements to an instance of SQL
Server. The current batch of statements is composed of all statements
entered since the last GO, or since the start of the ad hoc session or
script if this is the first GO.
By the way, in your case, a user-defined table function might be more appropriate than a stored procedure.

Customizable database names and TempDB

I have a lump of SQL that looks a little like this
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = '{FOO}')
BEGIN
EXECUTE ('CREATE DATABASE {FOO}')
ALTER DATABASE {FOO} SET AUTO_CLOSE OFF
END
{FOO} is replaced at runtime with the name of a user configurable database. The logic is that I don't want to create the database if it already exists.
If {FOO} is tempdb then I get a failure when the query runs
Option 'AUTO_CLOSE' cannot be set in database 'tempdb'.
My question is why do I get this failure? SELECT * FROM sys.databases WHERE name = 'tempdb' returns zero results so surely my whole BEGIN/END pair shouldn't run? Indeed, if I put a print statement between begin and end, I don't see any output.
My guess is that SQL Server is doing some kind of linting on the SQL to make sure I don't muck around with tempdb? I have solved the problem by using EXECUTE instead, but I'm a little confused why I have to!
Try ensuring both commands are separate and within dynamic SQL, then the change to tempdb won't be caught by the parser:
EXEC sp_executesql N'CREATE DATABASE {FOO};';
EXEC sp_executesql N'ALTER DATABASE {FOO} SET AUTO_CLOSE OFF;';
This is similar to the reason you can't do this:
IF 1 = 1
BEGIN
CREATE TABLE #t1(id INT);
END
ELSE
BEGIN
CREATE TABLE #t1(x NVARCHAR(255));
END
Even though you and I know that only one of those #t1 code paths will ever be reached, SQL Server presumes that both paths could be reached at runtime, and so complains at parse time.