你在 SQL Server 中做啥来创建或更改?

Posted

技术标签:

【中文标题】你在 SQL Server 中做啥来创建或更改?【英文标题】:What do you do in SQL Server to CREATE OR ALTER?你在 SQL Server 中做什么来创建或更改? 【发布时间】:2009-09-16 16:45:11 【问题描述】:

年份是 2009 年,SQL Server 没有 CREATE OR ALTER/REPLACE。这就是我所做的。

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES 
           WHERE ROUTINE_NAME = 'SynchronizeRemoteCatalog' 
             AND ROUTINE_SCHEMA = 'dbo' 
             AND ROUTINE_TYPE = 'PROCEDURE')
 EXEC ('DROP PROCEDURE dbo.SynchronizeRemoteCatalog')

CREATE PROCEDURE dbo.SynchronizeRemoteCatalog
AS BEGIN
    -- body
END

对于触发器,您必须依靠专有的系统视图。

这是目前最被接受的约定吗?

编辑: 正如 n8wrl 建议的那样,official word 表明此功能不是高优先级。于是就有了这个问题。

【问题讨论】:

看起来,短短七年多之后,SQL Server 现在has CREATE OR ALTER。哇哦。 @ruffin,是的,这就是我现在所做的。这里的方法仍然是早期版本(半衰期长)的最佳选择。 支持 CREATE 或 ALTER,但这仅适用于 SQL Server 2016。 【参考方案1】:

这篇文章很好地说明了在 SQL Server 中删除对象时丢失权限。

Tips ‘N’ Tricks – T-SQL – An Elegant way to CREATE or ALTER Stored Procedures in One-Go for Easy Maintenance

所以这里是保留权限的方法:

IF OBJECT_ID('spCallSomething') IS NULL
    EXEC('CREATE PROCEDURE spCallSomething AS SET NOCOUNT ON;')
GO

ALTER PROCEDURE spCallSomething ... 
--instead of DROP/CREATE

同样适用于函数,只需将上述代码中的PROCEDURE 替换为FUNCTION

考虑这样做的另一个原因是容忍失败。假设您的 DROP 成功,但您的 CREATE 失败 - 您以损坏的数据库结束。使用 ALTER 方法,您最终会得到旧版本的对象。

【讨论】:

是的,这正是我现在一直这样做的方式(AS SET NOCOUNT ON 除外,这是一个有趣的转折)。想想看,我应该为这个模式设置一个 yasn-p,因为我打字太多了。 我已经编写了模板来处理丢失的存储过程、标量函数和表函数的创建,并取得了很好的成功。 但是,如果您“将 PROCEDURE 替换为 FUNCTION”,则语法不正确。我已经发布了our examples below。 为什么“SET NOCOUNT ON”是临时占位符存储体的好选择?在这里提问:***.com/questions/40849374/… 我认为使用这种方法还有一个很好的理由是避免在已经设置的潜在权限方面出现错误,例如DBA 以外的人进行了更改,使用 drop/create 您最终可能会删除您不知道的授予权限。【参考方案2】:

年份是 2009 年,SQL Server 没有 CREATE OR ALTER/REPLACE。

今年是 2016 年,现在它在 SQL Server 2016 RTM 和 CREATE OR ALTER(在 2016 SP1 中引入)中确实有 DIE (Drop If Exists)。

首先以Drop If Exists 为例,需要使用这种方法重新应用权限的注意事项仍然适用。示例语法是

DROP PROCEDURE IF EXISTS dbo.SynchronizeRemoteCatalog

GO

CREATE PROCEDURE dbo.SynchronizeRemoteCatalog
AS
  BEGIN
      BODY:
  END 

GO

/*TODO: Reapply permissions*/

CREATE OR ALTER 保留权限。示例语法是

 CREATE OR ALTER PROCEDURE dbo.SynchronizeRemoteCatalog
 AS
 BEGIN
   BODY:
 END

corresponding MSSQL Tiger Team blog post 解释

CREATE OR ALTER 可用于可编程对象,例如:

存储过程(包括本地编译) FUNCTIONS(Transact-SQL,包括本机编译) 触发器 查看次数

但不能用于:

需要存储的对象(表、索引和索引视图) CLR 用户定义函数 不推荐使用的可编程对象(RULE 和 DEFAULT) 非可编程对象(例如 CREATE ASSEMBLY、CREATE TABLE 或 CREATE - SCHEMA)。在这些对象上,CREATE 和 从语法和可用性的角度来看,ALTER 非常不同。

【讨论】:

【参考方案3】:

我们遇到了需要更新远程站点的情况,但我们没有 DROP 权限。到目前为止,我们一直在使用 SSMS 2008 R2 中内置的“DROP and CREATE”脚本,但现在我们需要进行更改。 我们创建了三个模板,当我们需要更新存储过程或函数时,我们将它们放在适当的 ALTER 脚本之上:

—- Stored Procedure
IF OBJECT_ID('[dbo].[<Name_Of_Routine, , >]') IS NULL
EXEC('CREATE PROCEDURE [dbo].[<Name_Of_Routine, , >] AS SET NOCOUNT ON;')
EXEC('GRANT EXECUTE ON [<Name_Of_Routine, , >] TO Public AS dbo;')
GO

—- Scalar Function
IF OBJECT_ID('[dbo].[<Name_Of_Routine, , >]') IS NULL
EXEC('CREATE FUNCTION [dbo].[<Name_Of_Routine, , >] (@i INT) RETURNS INT AS BEGIN RETURN 0 END;')
EXEC('GRANT EXECUTE ON [<Name_Of_Routine, , >] TO Public AS dbo;')
GO

—- Table-based Function
IF OBJECT_ID('[dbo].[<Name_Of_Routine, , >]') IS NULL
EXEC('CREATE FUNCTION [dbo].[<Name_Of_Routine, , >] (@i INT) RETURNS @O TABLE(i INT) AS BEGIN INSERT INTO @O SELECT 0 RETURN END;')
GO

任何特殊权限都会在每次 CREATE 后编写脚本(不能为表函数分配权限)。之后,ALTER 不会更改它,如果他们添加或修改权限,它们仍然存在。这样做,很容易复制函数或存储过程的名称,并使用模板参数替换来自动完成这些脚本。

现在,我希望 Microsoft 的好心人将把它添加到他们的“脚本 ___ as”列表中,或者让我们能够创建自己的脚本,这样这个脚本就可以“嵌入”了

您可能希望在以下 SQL Server 反馈条目背后投入一些力量: https://connect.microsoft.com/SQLServer/feedback/details/344991/create-or-alter-statement。它似乎是少数仍可公开访问的产品之一,他们表示他们“已经开始对此进行可行性审查,以决定我们是否可以在不久的将来发货。”声音越多,这种情况发生的可能性就越大!

(更新:现在触发器和视图也使用以下代码)

-- Triggers
IF OBJECT_ID('[dbo].[<Name_Of_Trigger, , >]') IS NULL -- Check if Trigger Exists
    EXEC('CREATE TRIGGER [dbo].[<Name_Of_Trigger, , >] ON [<Name_Of_Table, , >] AFTER UPDATE AS SET NOCOUNT ON;') -- Create dummy/empty SP
GO

-- Views
IF OBJECT_ID('[dbo].[<Name_Of_View, , >]') IS NULL -- Check if View Exists
    EXEC('CREATE VIEW [dbo].[<Name_Of_View, , >] AS SELECT 1;') -- Create dummy/empty View
GO

【讨论】:

【参考方案4】:

每次开发人员写IF EXISTS(...) DROP 时,都会有一只海豹幼崽被打。您应该确切地知道数据库中的内容,并且您的升级脚本应该根据您的应用程序架构的当前版本酌情执行 CREATE 或 ALTER:Version Control and your Database。

【讨论】:

为什么?安全总比后悔好!您是否从未遇到过脚本失败,突然间您即将删除的存储程序仍在数据库中徘徊?我总是先做一个 IF EXISTS() 检查 - 只是为了安全起见!你从来没有让一个愚蠢的临时 DBA 多次而不是只运行一次脚本吗?? 我发现这是一种合理的方法,可以让我的部署脚本安全地运行多次。此外,如果我能在不着凉的情况下用棍棒打海豹的话…… 我想我不是生活在理想的世界里。如果脚本在客户端机器上引发错误,即使在版本控制和测试之后,我该怎么说? “太糟糕了。它应该有效,所以我无能为力。”我会倒闭的,莱姆斯。 @marc: 是的,愚蠢的 dbas...我只是想在这里计划一个种子,让人们在数据库中更多地考虑作为版本控制下的资源,而不是“让打开 ssms”并修改表!'。我知道在实践中不可能不依赖 if exists() (或者 objec_id 不为空,我实际上更喜欢 gbn )。我猜海豹幼崽注定要死了......北极邪恶的害虫 (意识到这是僵尸线程)我不确定你是否能回答这个问题,“你在 SQL Server 中做什么来创建或更改?”与“不要在 SQL Server 中创建或更改”。 ;^) 好吧,我的意思是,我猜你做过,但我不确定你应该... "CREATE OR ALTER Club 的第一条规则是,'不要创建或更改创建或更改俱乐部!'"【参考方案5】:

我会在 DROP 之前使用 OBJECT_ID(...) IS NOT NULL

对象标识符必须是唯一的,因此它可以在不使用系统表的情况下工作:

CREATE TRIGGER dbo.ExistingTable ON dbo.AnotherTable FOR UPDATE
AS 
SET NOCOUNT ON
GO

给予

Msg 2714, Level 16, State 2, Procedure MetaClass, Line 3
There is already an object named ExistingTable ' in the database.

由于我们使用源代码控制等方式,我通常使用 ALTER。

【讨论】:

我终于明白你在说什么了......虽然 OBJECT_ID 也是专有的,但这是一种更紧凑的方式来执行检查。 我不知道 OBJECT_ID 是专有的......不过它可能也在 Sybase 中 您将如何编写查询?如果对象 id 不为空,那又如何呢?放下物体? T-SQL中有这样的语句吗?【参考方案6】:

我总是alter 我的对象,因为drop 是一种非常糟糕的做法,如果对象无法创建(24/7 db!),以及其他海报所拥有的,可能会使您的数据库处于不良状态提到了核武器的权限。

Sublime、Atom 和 VS Code 等编辑器可以让您将代码 sn-ps 作为模板,以便快速生成您的骨架脚本。 SQL 2016 现在终于支持DROP IF EXISTS 构造,但它仍然从错误的方向接近 - 一切都是drop/create,而不是在遥远的过去一次性createalter 从那时起。此外,我已经尝试让我的标题尽可能短,所以我没有比create proc dbo.myproc as 更喜欢create 存根。

观看次数:

if objectproperty(object_id('dbo.myview'), 'IsView') is null begin
    exec('create view dbo.myview as select 1 c')
end
go
alter view dbo.myview as
    -- select *
    -- from table
go

过程:

if objectproperty(object_id('dbo.myproc'), 'IsProcedure') is null begin
    exec('create proc dbo.myproc as')
end
go
alter procedure dbo.myproc as
    set nocount on
    -- Add the stored proc contents here...
go

UDF(标量):

if objectproperty(object_id('dbo.myudf'), 'IsScalarFunction') is null begin
    exec('create function dbo.myudf returns int as begin return null end')
end
go
alter function dbo.myudf(@s varchar(100)) returns int as
begin
    -- return len(@s)
end
go

UDF(表格):

if objectproperty(object_id('dbo.myudf'), 'IsTableFunction') is null begin
    exec('create function dbo.myudf returns @t table(x int) as begin return end')
end
go
alter function dbo.myudf(@s varchar(100))
    returns @result table (
        -- Columns returned by the function
        id int identity(1, 1) primary key not null
        ,result varchar(100) null
    )
begin
    return
end
go

【讨论】:

谢谢,好点子。在正常运行时间很敏感的情况下,我已经开始在事务内部执行 DROP/CREATE,我认为这实际上与 ALTER 相同。但是,是的,ALTER 更具外科手术性。【参考方案7】:

这基本上就是这样做的方法,是的。我只是想知道您是否有特定的理由使用“EXEC”方法:

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'SynchronizeRemoteCatalog' AND ROUTINE_SCHEMA = 'dbo' AND ROUTINE_TYPE = 'PROCEDURE')
    EXEC ('DROP PROCEDURE dbo.SynchronizeRemoteCatalog')

为什么不只是:

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'SynchronizeRemoteCatalog' AND ROUTINE_SCHEMA = 'dbo' AND ROUTINE_TYPE = 'PROCEDURE')
    DROP PROCEDURE dbo.SynchronizeRemoteCatalog

???

对于触发器,有sys.triggers。这些是“sys”模式中的系统目录视图——实际上不是严格或直接的表。

马克

【讨论】:

我开始使用 EXEC 是因为有时当我将脚本发送到对象不存在 的客户端时,SQL Server 会抱怨。也许是迷信,但并没有伤害到任何东西。【参考方案8】:

今年是 2017 年,SQL Server 有 CREATE OR ALTER

SQL Server 2016 SP1 和 SQL Server vNext 具有新的 T-SQL 语言语句 - CREATE [OR ALTER] 用于:

存储过程 功能 触发器 查看次数

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2016/11/17/create-or-alter-another-great-language-enhancement-in-sql-server-2016-sp1/

【讨论】:

对于那些拥有 SQL 2016 SP1 及更高版本的幸运儿,这就是现在要走的路:)【参考方案9】:

我更喜欢 CREATE-ALTER 方法(不是语法)而不是 DROP-CREATE,原因有两个:

权限(使用DROP-CREATE,您必须重新创建它们) object_id(改变对象不会改变它)

示例DROP-CREATE

--Initial creation:
CREATE PROCEDURE dbo.my_proc
AS
SELECT *
FROM dbo.a
WHERE i < 10;
GO

SELECT OBJECT_ID('dbo.my_proc');
GO


-- Recreating
DROP PROCEDURE IF EXISTS dbo.my_proc;
GO

CREATE PROCEDURE dbo.my_proc
AS
-- some meaningless comment
SELECT *
FROM dbo.a
WHERE i < 10;
GO

SELECT OBJECT_ID('dbo.my_proc');
GO

DB Fiddle

我们可以看到object_id 发生了变化。

示例 2:CREATE-ALTER

-- Initial creation
CREATE PROCEDURE dbo.my_proc2
AS
SELECT *
FROM dbo.a
WHERE i < 10;
GO

SELECT OBJECT_ID('dbo.my_proc2');
GO

-- Altering
CREATE OR ALTER PROCEDURE dbo.my_proc2
AS
-- some meaningless comment
SELECT *
FROM dbo.a
WHERE i < 10;
GO

SELECT OBJECT_ID('dbo.my_proc2');
GO

DB Fiddle

在这种情况下,object_id 保持不变。


这可能会导致一些问题的示例场景。假设我们使用 SQL Server 2016 Query Store 并强制对存储过程使用特定的查询计划。

拖放创建

USE T1;
GO
-- make sure that Query Store is READ_WRITE 
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[a]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[a](
    [i] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [g] [uniqueidentifier] NULL,
    [z] VARCHAR(10)
);
END
GO

-- populate table (15k records)
INSERT INTO dbo.a(g, z)
SELECT NEWID(), number
FROM (SELECT CAST([key] AS INT) AS number 
    FROM OPENJSON( '[1' + REPLICATE(',1',3000-1)+']')
    ) AS num
GO 5

-- initial creation
CREATE PROCEDURE dbo.my_proc
AS
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO

-- Clustered Index Scan
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 

SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
GO
--dc1

-- creating index
CREATE NONCLUSTERED INDEX IX_dbo_a_z
ON dbo.a([z] ASC) INCLUDE ([i], [g]);
GO

-- index seek
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 

SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;

-- forcing plan GUI, clustered scan
-- dc3

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- dc4

-- Clustered Index Scan
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- dc5

/* MAIN PART  - DROP - RECREATE */
DROP PROCEDURE IF EXISTS dbo.my_proc;
GO

CREATE PROCEDURE dbo.my_proc
AS
-- some meaningless comment added by developer
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO

/* MAIN PART END */

-- Index Seek
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- object_id in query store is NULL
-- is_forced_plan flag is ignored !!!  

第一次执行:

添加索引并执行:

强制计划:

另一个执行:

DROP-CREATE 之后:


创建 - 更改

USE T2;
GO
-- make sure that Query Store is READ_WRITE 
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[a]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[a](
    [i] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [g] [uniqueidentifier] NULL,
    [z] VARCHAR(10)
);
END
GO

-- populate table (15k records)
INSERT INTO dbo.a(g, z)
SELECT NEWID(), number
FROM (SELECT CAST([key] AS INT) AS number 
    FROM OPENJSON( '[1' + REPLICATE(',1',3000-1)+']')
    ) AS num
GO 5

-- initial creation
CREATE PROCEDURE dbo.my_proc
AS
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO

-- Clustered Index Scan
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
-- ca1
GO

-- creating index
CREATE NONCLUSTERED INDEX IX_dbo_a_z
ON dbo.a([z] ASC) INCLUDE ([i], [g]);
GO

-- index seek
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
--ca2

-- forcing plan GUI
--ca3

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
--ca4

-- Clustered Index Scan
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;
--ca5
GO

/* MAIN PART  - CREATE-ALTER */
CREATE OR ALTER PROCEDURE dbo.my_proc
AS
-- some meaningless comment added by developer
SELECT *
FROM dbo.a
WHERE z LIKE '12%'
AND 1 = (SELECT 1);
GO

/* MAIN PART END */

-- Clustered Index Scan
EXEC dbo.my_proc;

EXEC sp_query_store_flush_db; 
SELECT qsq.query_id,
    qsq.query_text_id,
    qsq.context_settings_id,
    qsq.[object_id],
    OBJECT_NAME(qsq.[object_id]) AS [object_name],
    qsp.is_forced_plan,
    qsqt.query_sql_text,
    qsrs.count_executions,
    CAST(qsp.query_plan AS XbML) AS sql_query_plan
FROM sys.query_store_query qsq
JOIN sys.query_store_query_text qsqt
ON qsq.query_text_id = qsqt.query_text_id
JOIN sys.query_store_plan qsp
ON qsq.query_id= qsp.query_id
JOIN sys.query_store_runtime_stats qsrs
ON qsrs.plan_id = qsp.plan_id
WHERE query_sql_text LIKE '%dbo.a%'
AND qsq.[object_id] <> 0
ORDER BY qsq.query_id;

-- is_forced_plan is valid

第一次执行:

添加索引并执行:

强制计划:

另一个执行:

CREATE-ALTER 之后:

结果

使用 Drop-Create 我们失去了强制计划。

【讨论】:

【参考方案10】:

好像有一段时间了:link text

我的典型脚本:

IF EXISTS (SELECT name FROM sysobjects WHERE name = 'ig_InsertDealer' AND type = 'P')
    DROP PROC dbo.ig_InsertDealer
GO 
CREATE PROCEDURE dbo.ig_InsertDealer
...
GO
GRANT EXECUTE ON dbo.ig_InsertDealer TO ...
GO

【讨论】:

我建议使用 a)“sys”模式,b)更集中的系统视图,例如sys.tables 用于表,sys.triggers 用于触发器等,而不仅仅是通用的“sysobjects”(很快就会被弃用)。【参考方案11】:

我将根据上下文使用:我的初始构建或主要重构脚本将使用 check/drop/create,纯维护脚本使用 alter。

【讨论】:

【参考方案12】:

我有一个模板,它允许多次执行脚本而不会出错。

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[aaa_test]') AND type in (N'P', N'PC'))
    EXEC('CREATE PROCEDURE aaa_test AS')
    EXEC('GRANT EXECUTE ON aaa_test TO someone')
GO

ALTER PROCEDURE aaa_test 
     @PAR1 INT,
     @PAR2 INT=0
AS
BEGIN
    SELECT @PAR1 AS Par1, CASE @PAR2 WHEN 0 THEN 'Default' ELSE 'Other' END AS Par2
END
GO

执行:

EXEC aaa_test 1
EXEC aaa_test 1,5

【讨论】:

【参考方案13】:

你不应该放下一个物体。丢弃对象有两个问题:

1) 如果 CREATE 失败,您将不再拥有对象。 (您可以使用事务来避免这种情况,但会牺牲大量样板代码)

2) 如果您没有明确地重新创建对象,您将失去对该对象的权限。


我更喜欢在“如果不存在”条件下创建一个空白对象,然后使用 ALTER,并为此编写了帮助程序。

【讨论】:

【参考方案14】:

只给我的extend previous answer。

我更喜欢CREATE-ALTER 而不是DROP-CREATE 方法的另一个原因。它可能导致丢失有关对象的特定属性。例如ExecIsStartup:

USE master
GO

CREATE TABLE dbo.silly_logging(id INT IDENTITY(1,1) PRIMARY KEY
                               ,created_date DATETIME DEFAULT GETDATE()
                               ,comment VARCHAR(100));
GO

CREATE PROCEDURE dbo.my_procedure 
AS
INSERT INTO dbo.silly_logging(comment)
VALUES ('SQL Server Startup');
GO

-- mark procedure to start at SQL Server instance startup
EXEC sp_procoption @ProcName = 'dbo.my_procedure'
    , @OptionName = 'startup'   
    , @OptionValue = 'on';


SELECT name, create_date, modify_date, is_auto_executed
FROM master.sys.procedures
WHERE is_auto_executed = 1;
--name  create_date modify_date is_auto_executed
--my_procedure  2017-07-28 06:36:21.743 2017-07-28 06:36:24.513 1

现在让我们假设有人想使用DROP-CREATE 更新此过程:

DROP PROCEDURE dbo.my_procedure;
GO

CREATE PROCEDURE dbo.my_procedure 
AS
-- adding meaningless comment
INSERT INTO dbo.silly_logging(comment)
VALUES ('SQL Server Startup');
GO

SELECT name, create_date, modify_date, is_auto_executed
FROM master.sys.procedures
WHERE is_auto_executed = 1;
-- empty

如果您不知道或不检查,您最终会遇到无法启动的程序。

【讨论】:

以上是关于你在 SQL Server 中做啥来创建或更改?的主要内容,如果未能解决你的问题,请参考以下文章

你做啥来保持用户登录反应本机应用程序

错误 c3867,不知道我需要做啥来修复

我需要做啥来修复我的 Heroku 构建?

SQL Server R2 2008中的SQL Server Management Studio 阻止保存要求重新创建表的更改问题的设置方法

gcc 在这里做啥来为每个线程运行一次此代码?

compute() 在 dask 中做啥?