【问题标题】:How do I conditionally create a stored procedure in SQL Server?如何有条件地在 SQL Server 中创建存储过程?
【发布时间】:2008-09-10 23:45:27
【问题描述】:

作为我的集成策略的一部分,我运行了一些 SQL 脚本来更新数据库。所有这些脚本做的第一件事就是检查它们是否需要运行,例如:

if @version <> @expects
    begin
        declare @error varchar(100);
        set @error = 'Invalid version. Your version is ' + convert(varchar, @version) + '. This script expects version ' + convert(varchar, @expects) + '.';
        raiserror(@error, 10, 1);
    end
else
    begin
        ...sql statements here...
    end

效果很好!除非我需要添加存储过程。 “create proc”命令必须是一批sql命令中唯一的命令。在我的 IF 语句中添加“create proc”会导致此错误:

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

哎哟!如何将 CREATE PROC 命令放入我的脚本中,并仅在需要时才执行?

【问题讨论】:

    标签: sql-server stored-procedures


    【解决方案1】:

    这是我想出的:

    将其包装在 EXEC() 中,如下所示:

    if @version <> @expects
        begin
            ...snip...
        end
    else
        begin
            exec('CREATE PROC MyProc AS SELECT ''Victory!''');
        end
    

    像魅力一样工作!

    【讨论】:

    • 每次重新创建 procs(有条件地丢弃)是一个更好的解决方案。如果使用 exec,您将一无所获,并且有缺点;您的 proc 必须转义字符串,并且错误中的任何行号都将引用相对于 exec 命令的行号。
    • +1 表示能够处理存在以外的条件(在我的情况下,@@Version 决定是否创建某些 proc)
    • +1 - 我用它来配置我的 CRUD 生成器以创建一个存根过程(如果它不存在),然后 然后 改变它。这允许我修改我的存储过程,构建 CRUD,但保留分配给现有过程的任何权限
    【解决方案2】:

    SET NOEXEC ON 是关闭部分代码的好方法

    IF NOT EXISTS (SELECT * FROM sys.assemblies WHERE name = 'SQL_CLR_Functions')
      SET NOEXEC ON
    GO
    CREATE FUNCTION dbo.CLR_CharList_Split(@list nvarchar(MAX), @delim nchar(1) = N',')
    RETURNS TABLE (str nvarchar(4000)) AS EXTERNAL NAME SQL_CLR_Functions.[Granite.SQL.CLR.Functions].CLR_CharList_Split
    GO
    SET NOEXEC OFF
    

    在这里找到: https://codereview.stackexchange.com/questions/10490/conditional-create-must-be-the-only-statement-in-the-batch

    附:另一种方法是 SET PARSEONLY { ON |离开 }。

    【讨论】:

    • 喜欢这个解决方案,因为我试图创建的过程中已经有动态 sql。动态创建动态 sql 并不好玩。
    • SET PARSEONLY ON 在解析时解释,而不是在执行时解释,并且在 if 语句中不起作用(即无论 IF 条件是真还是假,它都会打开它)
    【解决方案3】:

    但要注意存储过程中的单引号 - 它们需要通过添加第二个引号来“转义”。第一个答案已经做到了,但以防万一你错过了。年轻球员的陷阱。

    【讨论】:

    • 这个答案应该是评论。你为什么不把它移到那里?
    【解决方案4】:

    对数据库进行版本控制是可行的方法,但是... 为什么有条件地创建存储过程。对于视图、存储过程、函数,只需有条件地删除它们并每次重新创建它们。如果您有条件地创建,那么您将不会清理有问题或 2 年前由另一个开发人员(您或我永远不会这样做)放入的数据库,他确信他会记得删除一次紧急更新。

    【讨论】:

    • 因为重新创建会丢失创建日期等元数据
    【解决方案5】:

    删除和创建的问题是您丢失了以前应用于被删除对象的任何安全授权。

    【讨论】:

    • 如果您在重新创建程序后不重新添加这些授权,这只是一个问题;-)。
    【解决方案6】:

    这是一个旧线程,但Jobo 是错误的:Create Procedure 必须是批处理中的第一个语句。因此,您不能使用Exists 来测试是否存在,然后使用CreateAlter。可惜。

    【讨论】:

    • True :) (顺便说一句,这可能应该是评论,而不是答案。但我知道你还不能“评论”;)
    【解决方案7】:

    最好更改现有的存储过程,因为已添加的属性和权限可能会在删除存储过程时丢失。

    所以,测试它是否不存在,如果不存在则创建一个虚拟 proc。然后使用 alter 语句。

    IF NOT EXISTS(SELECT * FROM sysobjects WHERE Name = 'YOUR_STORED_PROC_NAME' AND xtype='P')
    EXECUTE('CREATE PROC [dbo].[YOUR_STORED_PROC_NAME] as BEGIN select 0 END')
    GO
    ALTER  PROC [dbo].[YOUR_STORED_PROC_NAME]
    ....
    

    【讨论】:

    • 不过,如果它存在的话,这个会改变程序。如果有人已经创建了它的更新版本怎么办?如何避免在已经存在的情况下更改过程,在不存在的情况下创建新的过程,以避免丢失现有的过程代码?
    • @JustAMartin,这不是版本控制,而只是一个 Alter 或 Create 语句。我不知道 SQL Server 或任何其他数据库的任何本机或内置版本控制。我认为人们需要创造一些东西。也许是自定义属性,存储过程本身的 cmets 中的某些东西?我的观点是,如果 proc 已经存在,则始终删除它可能会丢失创建时未恢复的重要属性。
    • @JustAMartin 我发现我误读了您的评论。如果一个人只希望在不存在同名的存储过程时创建一个存储过程,那么只需使用 create。如果 proc 已经定义,它将失败。
    • @JustAMartin 我发现我误读了您的评论。如果一个人只希望在不存在同名的存储过程时创建一个存储过程,那么只需使用 create。如果 proc 已经定义,它将失败。
    • 是的,这就是 MS SQL Server 的问题;没有明确的方法可以告诉它 - 如果存储过程存在,则跳过它(也不要抛出错误 - 只是跳过尝试更新它)。
    【解决方案8】:

    我必须承认,我通常会同意@Peter - 我每次都会有条件地放弃然后无条件地重新创建。过去,在尝试猜测数据库之间的架构差异时,无论是否有任何形式的版本控制,我都被抓住了太多次。

    话虽如此,您自己的建议@Josh 还是很酷的。当然很有趣。 :-)

    【讨论】:

      【解决方案9】:

      我的解决方案是检查 proc 是否存在,如果存在则删除它,然后创建 proc(与 @robsoft 的答案相同,但有一个示例......)

      IF EXISTS(SELECT * FROM sysobjects WHERE Name = 'PROC_NAME' AND xtype='P') 
      BEGIN
          DROP PROCEDURE PROC_NAME
      END
      GO
      CREATE PROCEDURE PROC_NAME
          @value  int
      AS
      BEGIN
          UPDATE  SomeTable
          SET     SomeColumn = 1
          WHERE   Value = @value
      END
      GO
      

      【讨论】:

      • 因此,如果该过程存在,我们将删除它。但是,如果它是更新、更好的程序版本呢?盲目地删除和覆盖它是不好的。在很多情况下,如果它已经存在而我们没想到它会存在,那么就不要管它,做一些调查,看看它是从哪里来的,你是否真的可以覆盖它。
      • 我的问题是为什么人们在服务器上编辑脚本而不将这些更改提交到源代码管理。他们怎么知道他们的更改不会被其他演员盲目地覆盖?如果此解决方案对您不起作用,请不要使用它,但是来这里抱怨它不适合您的特定用例对任何人都没有帮助,老实说,如果您有人直接在您的 SQL 上编辑过程如果不将这些更改提交到源代码管理服务器,那么 IMO 会遇到更大的问题。
      • 我不是在抱怨,只是在警告,因为当一些大型旧项目管理混乱并且有两个或多个不同公司的独立开发人员维护相同的副本时,我一直处于令人讨厌的境地不同环境下的数据库。因此,存储过程可能存在于一台服务器上但不存在于另一台服务器上,因此有必要应用一个根本不做任何事情(不失败,不改变)的脚本,以防存储过程已经存在,并创建一个新的如果没有。但是,是的,你是对的,如果你有良好的管理和单一的权力来源,那就没有必要了。
      【解决方案10】:

      使用 T-SQL 中的“Exists”命令查看存储的过程是否存在。如果是,请使用“Alter”,否则使用“Create”

      【讨论】:

        【解决方案11】:
        IF NOT EXISTS(SELECT * FROM sys.procedures WHERE name = 'pr_MyStoredProc')
        BEGIN
        
             CREATE PROCEDURE pr_MyStoredProc AS .....
             SET NOCOUNT ON
        END
        
        ALTER PROC pr_MyStoredProc
        AS
        SELECT * FROM tb_MyTable
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-16
          • 2018-01-16
          • 1970-01-01
          • 2010-09-09
          相关资源
          最近更新 更多