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

The*_*per 266 sql t-sql sql-server stored-procedures

我有一个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
...
Run Code Online (Sandbox Code Playgroud)

如何添加检查存储过程是否存在并创建它(如果它不存在)但如果存在则更改它?

Geo*_*off 436

我意识到这已被标记为已回答,但我们过去常常这样做:

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
  ....
Run Code Online (Sandbox Code Playgroud)

只是为了避免丢弃程序.

  • 只是添加一些关于为什么这是一个好主意的注释:1)drop将清除所有安全设置,2)通过这种方式执行,如果alter脚本由于某种原因失败,sp将不会被删除. (71认同)
  • 这是真正正确的答案.它避免丢失有问题的存储过程中的任何GRANTS. (10认同)
  • 这种方法有很大的好处,因为没有存储过程不存在的时间点.如果将更新应用于关键系统,而其他人,系统或线程仍在使用,则这可能至关重要.追踪由于暂时丢弃存储过程而导致的错误可能非常令人烦恼,因为它们很难再现. (6认同)
  • 这是一个很好的解决方案,因为已经提到过很多原因,我想补充一点,如果DBA依赖于proc元数据(比如创建日期),这会使这些东西保持原样,而不是制作proc每次都是全新的.我试图将这变成我团队的"最佳实践",用于维护我们自己的过程,通常必须将其复制/传播到多个DB. (3认同)
  • 还要考虑一些人_want_`GRANT`语句在脚本中明确表示*如果他们改变了*; 所以仍然有理由使用`DROP`而不是'ALTER`. (2认同)
  • 为什么`SET NOCOUNT ON` 是临时存储过程体的好选择?在这里问:http://stackoverflow.com/questions/40849374/is-set-nocount-on-a-good-choice-for-a-placeholder-stored-procedure-body (2认同)

Qua*_*noi 181

您可以在任何能够运行查询的位置运行过程代码.

只需复制以下内容AS:

BEGIN
    DECLARE @myvar INT
    SELECT  *
    FROM    mytable
    WHERE   @myvar ...
END
Run Code Online (Sandbox Code Playgroud)

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

这就像所谓的匿名程序一样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 …
Run Code Online (Sandbox Code Playgroud)

更新:

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

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

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

更新:

在SQL Server 2016+中,您可以这样做

CREATE OR ALTER PROCEDURE dbo.MyProc

  • 在不使用动态SQL时,CREATE PROC语句必须是批处理中的唯一语句,因此在以这种方式实现时,无法围绕DROP/CREATE包装事务.在DROP PROC调用之后必须有一个GO(批处理分隔符). (2认同)
  • “创建或更改程序”让我的生活变得更加简单 - 谢谢! (2认同)

小智 120

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

IF (OBJECT_ID('MyProcedure') IS NOT NULL)
  DROP PROCEDURE MyProcedure
GO
Run Code Online (Sandbox Code Playgroud)

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

我希望这有帮助!

  • 更好:IF(OBJECT_ID('MyProcedure','P')不是NULL)DROP PROCEDURE MyProcedure GO (62认同)

Mic*_*rie 30

我知道你想"改变一个程序,如果它存在,只删除它,如果它不存在"但我相信只是总是丢弃程序然后重新创建它更简单.以下是仅在已存在的情况下删除该过程的方法:

IF OBJECT_ID('MyProcedure', 'P') IS NOT NULL
    DROP PROCEDURE MyProcedure
GO
Run Code Online (Sandbox Code Playgroud)

第二个参数告诉OBJECT_ID仅查找与对象object_type = 'P',它们是存储过程:

AF =聚合函数(CLR)

C = CHECK约束

D =默认(约束或独立)

F = FOREIGN KEY约束

FN = SQL标量函数

FS =汇编(CLR)标量函数

FT =汇编(CLR)表值函数

IF = SQL内联表值函数

IT =内部表格

P = SQL存储过程

PC =汇编(CLR)存储过程

PG =计划指南

PK = PRIMARY KEY约束

R =规则(旧式,独立)

RF =复制过滤器过程

S =系统基表

SN =同义词

SO =序列对象

TF = SQL表值函数

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

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'
Run Code Online (Sandbox Code Playgroud)


Hyb*_*s95 22

从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


gkb*_*gkb 19

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

create or alter procedure procTest
as
begin
 print (1)
end;
go
Run Code Online (Sandbox Code Playgroud)

如果尚不存在,则创建存储过程,但如果存在则更改它.

参考


小智 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
    
    Run Code Online (Sandbox Code Playgroud)
  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 ...
    
    Run Code Online (Sandbox Code Playgroud)
  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 ...
    
    Run Code Online (Sandbox Code Playgroud)


Shi*_*hiv 5

这是一种方法,并以这种方式使用它背后的一些推理.编辑存储过程并不是很好,但有利有弊......

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

1)避免改变过程决策.我们的流程总是如果EXISTS DROP THEN CREATE.如果你采用相同的模式来假设新的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
Run Code Online (Sandbox Code Playgroud)


Rom*_*ain 5

在 Sql server 2008 以后,你可以使用“ INFORMATION_SCHEMA.ROUTINES

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES 
  WHERE ROUTINE_NAME = 'MySP'
        AND ROUTINE_TYPE = 'PROCEDURE') 
Run Code Online (Sandbox Code Playgroud)