【问题标题】:DROP...CREATE vs ALTER删除...创建与更改
【发布时间】:2018-07-20 17:30:08
【问题描述】:

在创建存储过程、视图、函数等时,对对象执行 DROP...CREATE 还是 ALTER 更好?

我看到许多“标准”文档声明要执行 DROP...CREATE,但我看到许多 cmets 和主张使用 ALTER 方法的论点。

ALTER 方法保持安全性,而我听说 DROP...CREATE 方法在第一次执行时强制对整个 SP 进行重新编译,而不仅仅是语句级别的重新编译。

有人可以告诉我使用其中一种是否还有其他优点/缺点?

【问题讨论】:

  • 后续问题:DROP...CREATE 仍然是原子的吗?我假设 ALTER 是原子的,因为它是单个语句,但也值得验证。

标签: sql sql-server tsql


【解决方案1】:

ALTER 也会强制重新编译整个过程。语句级重新编译适用于过程内的语句,例如。单个 SELECT,由于基础表更改而重新编译,而对过程没有任何更改。甚至不可能选择性地重新编译 ALTER 过程中的某些语句,以便了解在 ALTER 过程之后服务器必须...编译它。什么在 SQL 文本中发生了变化。

对于所有对象,ALTER 总是更好,因为它保留了所有安全性、所有扩展属性、所有依赖项和所有约束。

【讨论】:

    【解决方案2】:

    这就是我们的做法:

    if object_id('YourSP') is null
        exec ('create procedure dbo.YourSP as select 1')
    go
    alter procedure dbo.YourSP
    as
    ...
    

    如果“存根”存储过程尚不存在,代码将创建它,否则它会进行更改。这样,即使您重复执行该脚本,该过程的任何现有权限都将被保留。

    【讨论】:

    • 最好做 object_id('dbo.YourSP') 否则你最终可能会改变一个不存在的表,除非有另一个所有者
    • 函数呢?
    • @DannyRancher 我用IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id('FunctionName') AND xtype IN ('FN', 'IF', 'TF'));确保它存在并且它是一个函数。创建函数存根比较棘手,因为标量值函数、内联表值函数和多语句表值函数都是不同的类型。
    • @marc_s 终于happening
    • @MadSkunk:很有趣!感谢您的链接!!
    【解决方案3】:

    更改通常会更好。如果您删除并创建,您可能会失去与该对象关联的权限。

    【讨论】:

    • 诺诺! WITH RECOMPILE 告诉 SQL Server 在存储过程的每次执行 上放弃查询计划。下次运行 sproc 时,所有 ALTER 都会导致重新编译。
    • 为安多马尔辩护,在他发表评论后,我改变了答案。他在写信,而我正在回答问题的不同部分,而我的答案不清楚。
    【解决方案4】:

    从 SQL Server 2016 SP1 开始,您现在可以选择将 CREATE OR ALTER 语法用于存储过程、函数、触发器和视图。请参阅 SQL Server 数据库引擎博客上的 CREATE OR ALTER – another great language enhancement in SQL Server 2016 SP1。例如:

    CREATE OR ALTER PROCEDURE dbo.MyProc
    AS
    BEGIN
        SELECT * FROM dbo.MyTable
    END;
    

    【讨论】:

    • 不确定为什么您的答案没有更多的赞成票。我知道大多数客户通常使用旧版本的 SQL Server,但对于新版本来说,这应该是最重要的。
    【解决方案5】:

    例如,如果您有一个经常从网站调用的函数/存储过程,则可能会导致问题。

    存储的过程将被丢弃几毫秒/秒,在此期间,所有查询都将失败。

    如果你做一个改变,你就不会有这个问题。

    新创建的存储过程的模板通常是这种形式:

    IF EXISTS (SELECT * FROM sysobjects WHERE type = 'P' AND name = '<name>')
        BEGIN
            DROP PROCEDURE <name>
        END
    GO
    
    CREATE PROCEDURE <name>
    ......
    

    但是,相反的更好,imo:

    如果 storedproc/function/etc 不存在,请使用虚拟 select 语句创建它。然后,alter 将始终有效 - 它永远不会被丢弃。

    我们有一个存储过程,所以我们的存储过程/函数通常是这样的:

    EXEC Utils.pAssureExistance 'Schema.pStoredProc'
    GO
    
    ALTER PROCECURE Schema.pStoredProc
    ...
    

    我们对函数使用相同的存储过程:

    EXEC Utils.pAssureExistance 'Schema.fFunction'
    GO
    
    ALTER FUNCTION Schema.fFunction
    ...
    

    在 Utils.pAssureExistance 中,我们执行 IF 并查看“.”之后的第一个字符:如果是“f”,我们创建一个虚拟函数,如果是“p”,我们创建一个虚拟存储过程。

    但请注意,如果您创建一个虚拟标量函数,并且您的 ALTER 位于表值函数上,则 ALTER FUNCTION 将失败,说明它不兼容。

    同样,Utils.pAssureExistance 可以很方便,带有一个额外的可选参数

    EXEC Utils.pAssureExistance 'Schema.fFunction', 'TableValuedFunction'
    

    将创建一个虚拟表值函数,

    另外,我可能是错的,但我认为如果您执行删除过程并且查询当前正在使用存储过程,它将失败。

    但是,alter 过程将等待所有查询停止使用存储过程,然后对其进行更改。如果查询“锁定”存储过程的时间过长(比如几秒钟),ALTER 将停止等待锁定,并且无论如何都会更改存储过程:使用存储过程的查询可能会在那时失败。

    【讨论】:

    • 为什么不直接签入“sys.procedures”而不是使用 sysobjects(已弃用)并且必须指定类型??
    • "sysobjects" 自 SQL Server 2008 起已弃用:msdn.microsoft.com/en-us/library/ms143729.aspx
    • 好吧...我使用 SQL Server 2008 中的模板创建了一个存储过程,并复制粘贴了它。我猜微软应该遵循他们自己的指导方针!
    【解决方案6】:

    我不知道是否可以做出这样的笼统评论并说“ALTER 更好”。我认为这一切都取决于情况。如果您需要这种细化到过程级别的权限,您可能应该在单独的过程中处理它。不得不放弃并重新创建是有好处的。它清除了现有的安全性并将其重置为可预测的内容。

    我一直更喜欢使用 drop/recreate。我还发现将它们存储在源代码管理中更容易。而不是做......如果存在则改变,如果不存在则创建。

    话虽如此...如果您知道自己在做什么...我认为这并不重要。

    【讨论】:

      【解决方案7】:

      如果您执行 DROP,然后使用 CREATE,您几乎拥有 与使用 ALTER VIEW 语句的效果相同。问题是您需要完全重新建立您对谁可以和不能使用视图的权限。 ALTER 保留任何依赖信息并设置权限。

      【讨论】:

      【解决方案8】:

      您提出了一个专门与不包含任何数据的数据库对象相关的问题,理论上不应该经常更改。

      您可能需要编辑这些对象,但不是每 5 分钟一次。因此,我认为您已经一锤定音 - 权限。

      简短的回答,不是真正的问题,只要权限不是问题

      【讨论】:

        【解决方案9】:

        DROP 通常会丢失权限和任何扩展属性。

        在某些 UDF 上,ALTER 也会丢失扩展属性(肯定是在 SQL Server 2005 多语句表值函数上)。

        我通常不会 DROPCREATE 除非我也在重新创建这些东西(或者知道我想失去它们)。

        【讨论】:

          【解决方案10】:

          我们曾经在开发工作时使用 alter,要么创建新功能,要么修改功能。当我们完成我们的开发和测试后,我们将进行放置和创建。这会修改 procs 上的日期/时间戳,以便您可以按日期/时间对它们进行排序。

          它还允许我们查看我们发送的每个交付物按日期捆绑的内容。

          【讨论】:

            【解决方案11】:

            如果存在则直接添加会更好,因为如果您在将脚本移至 QA 或测试或生产时有多个环境,则您不知道该脚本是否已存在于该环境中。通过添加一个 drop(如果它已经存在)然后添加,无论它是否存在,您都将被覆盖。然后您必须重新申请权限,但最好是听到您的安装脚本出错。

            【讨论】:

              【解决方案12】:

              从可用性的角度来看,drop and create 比alter 更好。在不包含该对象的数据库中,Alter 将失败,但如果存在 IF EXISTS DROP 然后 CREATE 将在对象已经存在的数据库中或在对象不存在的数据库中工作。在 Oracle 和 PostgreSQL 中,您通常使用语句 CREATE OR REPLACE 创建函数和过程,这与 SQL SERVER IF EXISTS DROP 和 CREATE 的作用相同。如果 SQL Server 采用这种小巧但非常方便的语法,那就太好了。

              我会这样做。针对给定对象将所有这些都放在一个脚本中。

              IF EXISTS ( SELECT 1
                          FROM information_schema.routines
                          WHERE routine_schema = 'dbo'
                            AND routine_name   = '<PROCNAME'
                            AND routine_type   = 'PROCEDURE' )
              BEGIN
                  DROP PROCEDURE <PROCNAME>
              END
              GO
              
              
              CREATE PROCEDURE <PROCNAME>
              AS
              BEGIN
              END
              GO
              
              GRANT EXECUTE ON <PROCNAME> TO <ROLE>
              GO
              

              【讨论】:

              • 你能解释一下-1吗?我很好奇这怎么没有价值?
              • 我认为你得到了 -1,因为你的陈述在另一个问题中被驳斥了。基本上有人不同意你的观点。
              猜你喜欢
              • 2019-05-16
              • 1970-01-01
              • 1970-01-01
              • 2011-12-28
              • 2021-10-02
              • 1970-01-01
              • 1970-01-01
              • 2016-11-29
              • 2013-10-08
              相关资源
              最近更新 更多