ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一个每次客户端执行“数据库管理”功能时都必须运行的 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 由于语法限制而无法工作。我能做些什么?

L
Lunyx

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

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/…
Q
Quassnoi

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

只需复制 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(批处理分隔符)。
M
MrChips

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

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

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

我希望这有帮助!


更好: IF (OBJECT_ID('MyProcedure', 'P') IS NOT NULL) DROP PROCEDURE MyProcedure GO
M
Michael Currie

我知道你想“如果它存在就改变一个过程,如果它不存在就创建它”,但我相信它更简单:

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

像这样:

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 = 复制-filter-procedure S = 系统基表 SN = 同义词 SO = 序列对象 TF = SQL 表值函数 TR = 触发器

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

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

TF 不见了。不过,+1 提供此列表
也是触发器的TR
“并且只有在它不存在时才将其删除”。如果它不存在,则无需删除它。 ;-) 更改为“创建它”。无法自己编辑。
C
Contango

从 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


g
gkb

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

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

如果不存在,这将创建一个存储过程,但如果存在则更改它。

Reference


我想强调这仅适用于 SQL Studio - 在 sql 文件中它对我来说失败。
J
JayJay

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]

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

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

我是这样解决的:

检查存储过程是否存在: IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='my_procedure') BEGIN print 'exists' -- 或者你想要的水 END ELSE BEGIN print 'doesn''texists' -- 或者你想要的水 END但是,“CREATE/ALTER PROCEDURE”必须是查询批处理中的第一条语句”仍然存在。我这样解决它: SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE -- 查看过程函数或任何你想要的......我最终得到这个代码: IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('my_procedure ')) BEGIN DROP PROCEDURE my_procedure END SET ANSI_NULLS ON 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
S
Shiv

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

更新:您还可以将整个调用包装在 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

R
Romil Kumar Jain

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

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

R
Rennish Joseph

**在 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

m
myroslav

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

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

d
dnxit

你为什么不走简单的路

    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 如果您查看已编辑的历史记录,您会发现它最初带有 '%'
C
Community

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

https://i.stack.imgur.com/7rInr.png


我认为 Management Studio 已经提供了这样的工具。它被称为“生成脚本”
S
Shaikh Noman Nasir

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

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 本身会处理已经存在的内容这是我一直对我所有的人所做的客户!


W
Wartari

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


如果程序存在,这将无济于事。如果存在,请求者想要更改过程,如果不存在,则创建它。