如何在创建存储过程之前检查它是否存在


282

我有一个SQL脚本,每次客户端执行“数据库管理”功能时都必须运行。该脚本包括在客户端数据库上创建存储过程。其中一些客户端在运行脚本时可能已经具有存储过程,而某些客户端可能没有。我需要将丢失的存储过程添加到客户端数据库中,但是我尝试修改T-SQL语法的大小无关紧要,

CREATE / ALTER PROCEDURE'必须是查询批处理中的第一条语句

在创建作品之前,我已经读过这些内容,但是我不喜欢那样做。

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'MyProc')
DROP PROCEDURE MyProc
GO

CREATE PROCEDURE MyProc
...

我该如何添加检查存储过程的存在并创建它(如果不存在),但是更改它(如果存在)呢?


2
不,它不起作用,因为它会创建一个存储过程,据称这不是您想要的。从我们所看到的,它也不会在完成后删除它,因此它肯定存储在该术语的所有方面。它是不是无关紧要的,为什么你需要一个非存储过程
大卫Hedlund的

您所说的“非存储”过程是什么意思?您所有的样本工作都是重新创建一个存储过程。这与您的问题有什么关系?
AakashM 2010年

好,我们去。问题是,我有一个巨大的SQL脚本,许多客户端使用该脚本,并且每次客户端执行我们的软件提供的“数据库管理”功能时,都必须彻底运行该脚本。因此,其中一些客户端在运行脚本时可能已经存储了过程,而有些则没有。我知道这很愚蠢,实际上我不需要此过程来保持存储状态,我可以检查它是否存在,如果不存在则可以创建它。但是,无论我尝试改变T-SQL语法多少都无所谓,总是有一个错误。
塑造者

每次他们运行脚本时,它将尝试再次创建该过程(不幸的是,所有内容都必须在同一.sql文件中编写脚本,包括创建过程调用)。如果不存在,那么由于语法限制,那么CREATE不起作用。我能做什么?
塑造者

Answers:


199

您可以在任何能够运行查询的地方运行过程代码。

只需复制以下内容AS

BEGIN
    DECLARE @myvar INT
    SELECT  *
    FROM    mytable
    WHERE   @myvar ...
END

此代码执行的操作与存储的proc完全相同,但不存储在数据库端。

这很像在中所谓的匿名过程PL/SQL

更新:

您的问题标题有点令人困惑。

如果只需要创建一个不存在的过程,那么您的代码就可以了。

以下是SSMS创建脚本中的输出:

IF EXISTS ( SELECT  *
            FROM    sys.objects
            WHERE   object_id = OBJECT_ID(N'myproc')
                    AND type IN ( N'P', N'PC' ) ) 
DROP 
CREATE 

更新:

包含架构时如何执行此操作的示例:

IF EXISTS ( SELECT * 
            FROM   sysobjects 
            WHERE  id = object_id(N'[dbo].[MyProc]') 
                   and OBJECTPROPERTY(id, N'IsProcedure') = 1 )
BEGIN
    DROP PROCEDURE [dbo].[MyProc]
END

在上面的示例中,dbo是架构。

更新:

在SQL Server 2016+中,您可以

CREATE OR ALTER PROCEDURE dbo.MyProc


是的,这是真的,但是您将放弃所有过程功能,因为不会存储过程,udfs,视图等,以免在查询中调用。(对不起,对其进行了编辑,在我的脑袋中X确实有意义))
Adriaan Stander 2010年

1
是的,但是您可以从其他过程中调用过程,或将其返回用作表的输入。
Adriaan Stander 2010年

@astander:您也可以从存储过程中调用匿名代码。要在中使用其输出INSERT,您还需要使用OPENROWSETOPENQUERY与匿名代码一起使用。当然,匿名代码也有缺点:例如,它仅在调用者的特权下运行。我的观点是,这是可能的,而不是首选的方式:)
Quassnoi 2010年

“如果只需要创建一个不存在的过程,那么您的代码就可以了。” 而这正是我想知道的。我尝试在实际脚本上使用SSMS Create来执行操作,但效果不佳。但是感谢Quassnoi,对于这个不清楚的问题,我感到抱歉。
The Shaper 2010年

2
当不使用动态SQL时,CREATE PROC语句必须是批处理中的唯一语句,因此,以这种方式实现时,不能将事务包装在DROP / CREATE周围。在DROP PROC调用之后必须有一个GO(批处理分隔符)。
2015年

449

我知道这已经被标记为已回答,但是我们以前是这样做的:

IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.MyProc'))
   exec('CREATE PROCEDURE [dbo].[MyProc] AS BEGIN SET NOCOUNT ON; END')
GO

ALTER PROCEDURE [dbo].[MyProc] 
AS
  ....

只是为了避免删除该过程。


74
只是添加一些有关为什么这是个好主意的注释:1)删除将清除所有安全设置,2)通过这种方式进行操作,如果alter script由于某种原因失败,则不会删除sp。
Ryan Guill

10
这确实是正确的答案。这样可以避免丢失有关所存储过程的任何格兰特。
Andy_Vulhop 2014年

7
这种方法有一个很大的好处,就是没有时间点不存在存储过程。如果将更新应用到关键系统,而其他人,系统或线程仍在使用它,则这可能至关重要。跟踪由于暂时删除存储过程而导致的错误可能非常烦人,因为它们很难重现。
詹姆斯

3
由于已经提到的许多原因,这是一个很好的解决方案,我想补充一点,以防万一DBA依赖proc元数据(例如created-date),这会使这些内容保持不变,而不是使proc每次都是全新的。我试图将其转变为团队维护自己的proc的“最佳实践”,通常将其复制/传播到多个DB。
NateJ

2
还考虑一些人希望GRANT脚本的陈述明确,以防改变 ; 因此,仍然有理由使用DROP代替ALTER
科迪·斯托特

123

如果您正在寻找在删除数据库对象之前检查数据库对象是否存在的最简单方法,则这是一种方法(示例使用SPROC,就像上面的示例一样,但是可以针对表,索引等进行修改):

IF (OBJECT_ID('MyProcedure') IS NOT NULL)
  DROP PROCEDURE MyProcedure
GO

这是快速而优雅的,但是您需要确保所有对象类型都具有唯一的对象名称,因为它没有考虑到这一点。

我希望这有帮助!


62
那更好:IF(OBJECT_ID('MyProcedure','P')不为空)DROP PROCEDURE MyProcedure GO
alerya

32

我知道您想“更改某个过程(如果存在),仅在不存在的情况下将其删除”,但我相信始终删除该过程然后重新创建它会更简单。仅当此过程已存在时,这是删除该过程的方法:

IF OBJECT_ID('MyProcedure', 'P') IS NOT NULL
    DROP PROCEDURE MyProcedure
GO

第二个参数告诉OBJECT_ID仅查找与对象object_type = 'P'这些存储过程:

AF =汇总函数(CLR)

C = CHECK约束

D =默认(约束或独立)

F =外键约束

FN = SQL标量函数

FS =装配(CLR)标量函数

FT =装配(CLR)表值函数

IF = SQL内联表值函数

IT =内部表

P = SQL存储过程

PC =程序集(CLR)存储过程

PG =计划指南

PK =主键约束

R =规则(旧式,独立式)

RF =复制过滤器过程

S =系统基本表

SN =同义词

SO =序列对象

TF = SQL表值函数

TR =触发

您可以通过以下方式获取选项的完整列表:

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'

1
TF丢失。不过,为提供此列表而+1
Crono

也是TR的触发条件
CarlosOro


23

我知道这是一个非常古老的帖子,但是由于它出现在搜索结果的顶部,因此为使用SQL Server 2016 SP1的用户添加了最新更新-

create or alter procedure procTest
as
begin
 print (1)
end;
go

如果尚不存在,则会创建一个存储过程,但如果存在,则会对其进行更改。

参考


1
这样,是如此有用。
AgentFire

我想强调的是,这仅适用于SQL Studio-在一个sql文件中,它对我而言失败。
詹姆斯·L

10

DROP IF EXISTS是SQL Server 2016的新功能

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/

DROP  PROCEDURE IF EXISTS dbo.[procname]

1
这不是SqlServer语法...,建议在人们开始投票之前删除答案,并避免让新手感到困惑。
Pawel Czapski

@PawelCz它对SQL Server 2016及更高版本有效,我改写了答案。感谢您的反馈!
JayJay

这没有回答原始帖子。自动删除和重新创建以及仅在不存在的情况下创建之间存在细微的差异。删除proc将会删除与其相关的安全性,而该安全性可能已经编写了脚本。
罗恩

7

我有同样的错误。我知道该线程已经死了,但是我想设置“匿名过程”之外的另一个选项。

我这样解决了:

  1. 检查存储过程是否存在:

    IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='my_procedure') BEGIN
        print 'exists'  -- or watever you want
    END ELSE BEGIN
        print 'doesn''texists'   -- or watever you want
    END
  2. 但是,"CREATE/ALTER PROCEDURE' must be the first statement in a query batch"仍然存在。我这样解决了:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. 我最终得到以下代码:

    IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('my_procedure'))
    BEGIN
        DROP PROCEDURE my_procedure
    END
    
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE PROCEDURE [dbo].my_procedure ...

如果只需要一行代码,例如DROP PROCEDURE ...,就不需要开始和结束了
Phillip Senn 2015年

警告:无论您输入什么函数名称(对于T-SQL),“检查存储过程是否存在”功能将始终返回“存在”。这是不可靠的检查。
瑞安·巴蒂斯通

更好的选择:如果存在(从sys.procedures WHERE name ='name_of_table_as_seen_in_sysprocedures'中选择1)开始选择-1作为'status'结束
Ryan Battistone

5

这是一种方法以及使用这种方法背后的一些原因。编辑存储的proc不太好,但是有优点和缺点...

更新:您还可以将整个调用包装在TRANSACTION中。在单个事务中包括许多可以全部提交或全部回滚的存储过程。包装在事务中的另一个优点是,只要其他SQL连接不使用READ UNCOMMITTED事务隔离级别,存储过程就始终存在!

1)避免像过程决策一样进行更改。我们的流程是:如果存在,然后再创建。如果您以相同的方式假设新的PROC是所需的proc,则适应更改会有点困难,因为您将拥有一个IF EXISTS ALTER CSEATE。

2)您必须将CREATE / ALTER作为批处理中的第一个调用,因此您不能在动态SQL之外的事务中包装一系列过程更新。基本上,如果您要运行整个过程更新堆栈或将它们全部回滚而不恢复数据库备份,则这是一种在单个批次中完成所有操作的方法。

IF NOT EXISTS (select ss.name as SchemaName, sp.name as StoredProc 
    from sys.procedures sp
    join sys.schemas ss on sp.schema_id = ss.schema_id
    where ss.name = 'dbo' and sp.name = 'MyStoredProc')
BEGIN
    DECLARE @sql NVARCHAR(MAX)

    -- Not so aesthetically pleasing part. The actual proc definition is stored
    -- in our variable and then executed.
    SELECT @sql = 'CREATE PROCEDURE [dbo].[MyStoredProc]
(
@MyParam int
)
AS
SELECT @MyParam'
    EXEC sp_executesql @sql
END


3

我显然没有投票或评论所需的声誉,但我只是想说Geoff使用EXEC(sp_executesql可能更好)的答案绝对是必经之路。删除然后重新创建存储过程将最终完成工作,但是在一段时间内,存储过程根本不存在,这可能非常糟糕,尤其是在某些情况下反复运行。我的应用程序遇到各种各样的问题,因为后台线程正在执行IF EXISTS DROP ... CREATE,而另一个线程正在尝试使用存储过程。


3

**在T-Sql中删除和重新创建存储的proc的最简单方法是**

Use DatabaseName
go
If Object_Id('schema.storedprocname') is not null
begin
   drop procedure schema.storedprocname
end
go

create procedure schema.storedprocname
as

begin
end

3

这是我使用的脚本。有了它,我避免了不必要的删除和重新创建存储的过程。

IF NOT EXISTS (
    SELECT *
    FROM sys.objects
    WHERE object_id = OBJECT_ID(N'[dbo].[uspMyProcedure]')
    )
BEGIN
  EXEC sp_executesql N'CREATE PROCEDURE [dbo].[uspMyProcedure] AS select 1'
END
GO

ALTER PROCEDURE [dbo].[uspMyProcedure] 
    @variable1 INTEGER  
AS
BEGIN
   -- Stored procedure logic
END


1

你为什么不走这样简单的路

    IF EXISTS(SELECT * FROM sys.procedures WHERE NAME LIKE 'uspBlackListGetAll')
    BEGIN
         DROP PROCEDURE uspBlackListGetAll
    END
    GO

    CREATE Procedure uspBlackListGetAll

.....


在此处使用LIKE%语句的坏主意。如果OP拥有不想删除的其他程序(例如uspBlackListGetAll_V2)怎么办?
戴夫·霍根

@DaveHogan我同意。但是他没有放进去%,所以LIKE表现得像是=
Diego Jancic

1
@DiegoJancic,如果您查看编辑的历史记录,您会发现它最初是带有'%'的
Dave Hogan


0

我想知道!为什么我不写整个查询

GO
create procedure [dbo].[spAddNewClass] @ClassName varchar(20),@ClassFee int
as
begin
insert into tblClass values (@ClassName,@ClassFee)
end

GO
create procedure [dbo].[spAddNewSection] @SectionName varchar(20),@ClassID       int
as
begin
insert into tblSection values(@SectionName,@ClassID)
end

Go
create procedure test
as
begin 
select * from tblstudent
end

我已经知道前两个过程已经存在sql将运行查询,将给出前两个过程的错误,但仍会创建最后一个过程SQl本身正在照顾已经存在的问题,这是我一直对我所做的一切客户!


-2

如果不存在“您的过程名称”()开始,则创建过程...结束


如果该程序存在,则不会执行任何操作。请求者希望更改该过程(如果存在),如果不存在则创建它。
兰迪·伽米奇
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.