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

Posted

技术标签:

【中文标题】如何在创建存储过程之前检查它是不是存在【英文标题】:How to check if a stored procedure exists before creating it如何在创建存储过程之前检查它是否存在 【发布时间】:2011-01-05 12:54:06 【问题描述】:

我有一个 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
...

如何添加对存储过程是否存在的检查,如果不存在则创建它,如果存在则更改它?

【问题讨论】:

不,它不起作用,因为这会创建一个据称不是您想要的存储过程。从我们所看到的,它也不会在完成后丢弃它,因此它肯定存储在术语的各个方面。 不是与为什么需要非存储过程无关 “非存储”过程是什么意思?您的示例所做的只是重新创建一个存储过程;这和你的问题有什么关系? 好的,我们开始吧。问题是,我有一个巨大的 SQL 脚本,许多客户都在使用它,并且每次客户执行我们软件提供的“数据库管理”功能时都必须彻底运行它。因此,其中一些客户端可能已经在运行脚本时存储了该过程,而有些则可能没有。我知道这很愚蠢,我实际上不需要这个过程来保持未存储状态,我可以检查它是否存在,如果不存在则创建它。但是,无论我如何尝试弯曲 T-SQL 语法,都会出现错误。 每次他们运行脚本时,它都会尝试再次创建过程(不幸的是,所有内容都必须在同一个 .sql 文件中编写脚本,包括创建过程调用)。 IF NOT EXISTS THEN CREATE 由于语法限制而无法工作。我能做什么? ***.com/questions/937908/…的可能重复 【参考方案1】:

我知道这已经被标记为已回答,但我们曾经这样做:

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
  ....

只是为了避免放弃程序。

【讨论】:

只是添加一些关于为什么这是一个好主意的注释:1) 删除将清除所有安全设置,2) 通过这样做,如果更改脚本由于某种原因失败,则 sp不会被丢弃。 这种方法有一个巨大的好处,因为没有时间点不存在存储过程。如果将更新应用于关键系统,而其他人、系统或线程仍在使用它,这可能至关重要。跟踪由于暂时删除存储过程而导致的错误可能非常令人烦恼,因为它们很难重现。 由于已经提到的许多原因,这是一个很好的解决方案,我想补充一点,如果 DBA 依赖于 proc 元数据(例如创建日期),这就留下了东西完好无损,而不是每次都使 proc 焕然一新。我正试图将其转变为我的团队维护我们自己的过程的“最佳实践”,通常必须将其复制/传播到多个数据库。 还要考虑到有些人想要在脚本中明确显示GRANT 语句以防他们改变;所以仍然有理由使用DROP 而不是ALTER 为什么SET NOCOUNT ON 是临时存储过程体的好选择?在这里提问:***.com/questions/40849374/…【参考方案2】:

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

复制AS之后的所有内容:

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

这段代码做的事情与存储过程完全相同,但不存储在数据库端。

这很像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

【讨论】:

是的,这是真的,但是您将失去所有过程功能,因为不会存储任何过程、udf、视图等以从查询中调用。 (抱歉,编辑了它,它在我的脑海中确实有意义 X-)) 可以,但是您可以从其他过程中调用过程,或者将它们的返回值用作表的输入。 @astander:您也可以从存储过程中调用匿名代码。要在INSERT 中使用他们的输出,您需要使用OPENROWSETOPENQUERY,这也适用于匿名代码。当然,匿名代码也有缺点:例如,它只能在调用者的权限下运行。我的观点是这是可能的,而不是首选的做事方式:) "如果你只需要创建一个不存在的过程,那么你的代码就可以了。"这正是我想知道的。我尝试在实际脚本上使用 SSMS Create,但没有任何好处。但是感谢 Quassnoi,对于这个不清楚的问题,我很抱歉。 在不使用动态 SQL 时,CREATE PROC 语句必须是批处理中的唯一语句,因此当以这种方式实现时,您无法围绕 DROP/CREATE 包装事务。在 DROP PROC 调用之后必须有一个 GO(批处理分隔符)。【参考方案3】:

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

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

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

我希望这会有所帮助!

【讨论】:

那更好:IF (OBJECT_ID('MyProcedure', 'P') IS NOT NULL) DROP PROCEDURE MyProcedure GO【参考方案4】:

我知道你想“如果存在则更改过程,如果不存在则创建它”,但我相信这样做更简单:

    删除该过程(如果它已经存在),然后 重新创建它。

像这样:

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

CREATE PROCEDURE MyProcedure AS
BEGIN
    /* ..... */
END
GO

第二个参数告诉OBJECT_ID 只查找具有object_type = 'P'、which are stored procedures: 的对象

AF = 聚合函数 (CLR)

C = CHECK 约束

D = DEFAULT(约束或独立)

F = FOREIGN KEY 约束

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'

【讨论】:

TF 丢失。不过,为提供此列表 +1 触发器也是 TR "并且只有在它不存在时才将其删除"。如果它不存在,则无需删除它。 ;-) 更改为“创建它”。无法自己编辑。【参考方案5】:

从 SQL SERVER 2016 开始,您可以使用新的 DROP PROCEDURE IF EXISTSDROP PROC | PROCEDURE [ IF EXISTS ] [ schema_name. ] procedure [ ,...n ]

参考: https://msdn.microsoft.com/en-us/library/ms174969.aspx

【讨论】:

【参考方案6】:

我知道这是一篇很老的帖子,但由于它出现在热门搜索结果中,因此为使用 SQL Server 2016 SP1 的用户添加了最新更新 -

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

如果不存在则创建存储过程,如果存在则更改它。

Reference

【讨论】:

我想强调这只适用于 SQL Studio - 在 sql 文件中它对我来说失败了。【参考方案7】:

如果存在则删除 是 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]

【讨论】:

这不是 SqlServer 语法...,建议在人们开始投票之前删除答案,并避免新手混淆。 @PawelCz 它适用于 SQL Server 2016 及更高版本,我改写了答案。感谢您的反馈! 这不回答原帖。自动删除和重新创建与仅在不存在时创建之间存在细微差别。删除 proc 将删除与其关联的安全性,这可能是脚本化的。 @Ron 不仅需要重新创建安全性,还需要重新创建相关的扩展属性。 Object_id 也改了***.com/a/45000500/5070879【参考方案8】:

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

我是这样解决的:

    检查存储过程是否存在:

    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
    

    但是"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 ...
    

    我最终得到了这个代码:

    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 ...
    

【讨论】:

如果只有 1 行代码,例如 DROP PROCEDURE,则不需要开始和结束... 警告:“检查存储过程是否存在”函数将始终返回“存在”,无论您输入什么函数名(对于 T-SQL)。这是一个不可靠的检查。 更好的选择:IF EXISTS(SELECT 1 FROM sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') BEGIN select -1 as 'status' END【参考方案9】:

这是一种方法以及以这种方式使用它的一些原因。编辑存储过程并不那么漂亮,但有利有弊......

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

1) 避免将更改作为流程决策。我们的流程始终是“如果存在则删除然后创建”。如果您采用相同的模式假设新的 PROC 是所需的 proc,则满足更改会有点困难,因为您将拥有一个 IF EXISTS ALTER ELSE CREATE。

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

【讨论】:

【参考方案10】:

在 Sql server 2008 及以后的版本中,您可以使用“INFORMATION_SCHEMA.ROUTINES

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES 
  WHERE ROUTINE_NAME = 'MySP'
        AND ROUTINE_TYPE = 'PROCEDURE') 

【讨论】:

【参考方案11】:

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

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

【讨论】:

【参考方案12】:

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

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

【讨论】:

【参考方案13】:

你为什么不走简单的路像

    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 的行为就像= @DiegoJancic 如果您查看已编辑的历史记录,您会发现它最初带有 '%'【参考方案14】:

除了@Geoff 的回答之外,我还创建了一个简单的工具,它可以生成一个 SQL 文件,其中包含存储过程、视图、函数和触发器的语句。

见MyDbUtils @ CodePlex。

【讨论】:

我认为 Management Studio 已经提供了这样的工具。它被称为“生成脚本”【参考方案15】:

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

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 本身会处理已经存在的东西,这就是我一直对所有客户所做的事情!

【讨论】:

【参考方案16】:

CREATE Procedure IF NOT EXISTS 'Your proc-name' () BEGIN ... END

【讨论】:

如果过程存在,这将不起作用。如果存在,请求者想要更改过程,如果不存在,则创建它。

以上是关于如何在创建存储过程之前检查它是不是存在的主要内容,如果未能解决你的问题,请参考以下文章

如何在创建存储过程之前检查 PERVASIVE 数据库中是不是存在存储过程?

如何检测存储过程是不是已存在

如果存储过程尚不存在,则创建它,但不要更改也不要删除现有的存储过程

SQLSERVER创建该存储过程时不会出错,但是执行存储过程时报错

存储过程实现规范

如果尚不存在则创建存储过程,但是不要更改并且不要删除现有的存储过程