【问题标题】: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 由于语法限制而无法工作。我能做什么?

标签: sql sql-server tsql stored-procedures


【解决方案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 是临时存储过程体的好选择?在这里提问:stackoverflow.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】:

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

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

像这样:

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 EXISTS
DROP { 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 也改了stackoverflow.com/a/45000500/5070879
    【解决方案8】:

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

    我是这样解决的:

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

    【讨论】:

    • 如果只有 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

              【讨论】:

              • 如果过程存在,这将不起作用。如果存在,请求者想要更改过程,如果不存在,则创建它。
              猜你喜欢
              • 2015-08-11
              • 1970-01-01
              • 1970-01-01
              • 2011-08-21
              • 1970-01-01
              • 2011-08-08
              • 1970-01-01
              • 1970-01-01
              • 2020-07-29
              相关资源
              最近更新 更多