您在SQL Server中做什么以进行创建或更改?


67

年份为2009,SQL Server没有CREATE或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所建议的,官方用词表明此功能不是一个高度优先事项。因此是一个问题。


4
看起来,短短七年多之后,SQL Server现在有了CREATE OR ALTER。呜呼。
鲁芬

3
@ruffin,是的,那是我现在要做的。对于早期版本(具有较长的半衰期),此处的方法仍然是最佳选择。
哈波

6
对于CREATE OR ALTER,是的,但这仅是SQL Server 2016。
Icegras

Answers:


98

本文很好地介绍了在SQL Server中删除对象时丢失权限的问题。

因此,这里是保留权限的方法:

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

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

也适用于功能,只需更换PROCEDUREFUNCTION在上面的代码。

考虑这样做的另一个原因是对失败的容忍度。假设您的DROP成功,但是您的CREATE失败-您以损坏的DB结尾。使用ALTER方法,您将获得该对象的旧版本。


1
是的,这就是我现在已经做了一段时间了(除了AS SET NOCOUNT ON,这是一个有趣的转折)。想一想,我应该为此模式设置一个Yasnippet,因为我输入了很多内容。
harpo 2015年

我编写了一些模板来处理丢失的存储过程,标量函数和表函数的创建,并取得了成功。

但是,如果“仅将PROCEDURE替换为FUNCTION”,则语法不正确。我在下面发布了我们的示例

为什么“ SET NOCOUNT ON”是临时占位符存储过程主体的好选择?在这里问:stackoverflow.com/questions/40849374/…–
乔恩·施耐德

1
我认为使用此方法的另一个绝佳原因是避免有关已设置的潜在特权的错误,例如,除DBA之外的其他人进行了更改,使用drop / create可能会最终删除您不知道的授予的特权。
LeBaptiste

47

年份为2009,SQL Server没有CREATE或ALTER / REPLACE。

年份是2016年,现在它在SQL Server 2016 RTM和(2016 SP1中引入)中确实具有DIE(如果存在则掉线CREATE OR ALTER)。

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

相应的MSSQL虎团队博客文章解释

CREATE OR ALTER可以在可编程对象中使用,例如:

  • 存储过程(包括本地编译)
  • 功能(Transact-SQL,包括本机编译的)
  • 触发
  • 意见

但不能用于:

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

6

每次开发人员写IF EXISTS(...) DROP海豹幼崽时,都会被压成一团。您应该确切了解数据库中的内容,并且升级脚本应根据应用程序架构的当前版本:Version Control和Database适当地执行CREATe或ALTER 。


8
为什么?比后悔更安全!您是否从未遇到过脚本失败,而突然间您要删除的程序仍然在数据库中徘徊?为了安全起见,我总是会先进行IF EXISTS()检查!您是否从未有过愚蠢的temp-DBA多次运行某些脚本而不是一次运行?
marc_s 2009年

13
我发现这是一种允许我的部署脚本安全地运行多次的合理方法。此外,如果我能在不感冒的情况下进行封印...
n8wrl

@marc_s:如果您拥有强大的控制权并依靠ALTER(像我们一样),则风险较小。我提供给DBA的任何脚本都可以安全运行多次,或者是原子的,因此第二次出错。
gbn

3
我想我不生活在理想世界中。如果脚本在版本控制和测试后仍在客户端计算机上引发错误,我该怎么说?“太糟糕了。它应该可以工作,所以我无能为力。” 我会倒闭的,雷木思。
harpo

3
@marc:是的,愚蠢的dbas ...我只是想在这里计划一个种子,让ppl在db上更多地将其视为版本控制下的资源,而不是“让ssms打开并修改表!”。我知道在实践中不不可能不依赖if存在()(或者objec_id不为null,我实际上更喜欢gbn)。我想海豹幼崽注定要死...北极有害生物
Remus Rusanu

6

我们遇到了需要更新远程站点的情况,但是我们没有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之后,将对所有特殊权限进行脚本化(无法为Table函数分配权限)。在那之后,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

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

对象标识符必须是唯一的,因此无需使用系统表即可工作:

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也是专有的,但这是执行检查的一种更为紧凑的方法。
harpo

我不知道OBJECT_ID是专有的...虽然它也可能在Sybase中
gbn

您将如何编写查询?如果对象ID不为null,那该怎么办?放下物体?在T-SQL中是否有这样的声明?
Neolisk

3

我总是使用alter我的对象,因为adrop确实是一种不好的做法,并且如果对象创建失败(24/7 db!)以及其他张贴者提到的有关nuking权限的内容,可能会使DB处于不良状态。

诸如Sublime,Atom和VS Code之类的编辑器将使您将代码片段作为模板,以使其快速生成骨架脚本。SQL 2016现在终于支持DROP IF EXISTS构造了,但是它仍然朝着错误的方向前进-在遥远的过去和之后,一切都是一次drop/create而不是一次。另外,我试图使标题尽可能短,所以我没有比存根更好的了。createaltercreate proc dbo.myproc ascreate

观看次数:

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

1
谢谢,很好。如果正常运行时间很敏感,我会在事务内部执行DROP / CREATE,我认为这实际上与ALTER相同。但是是的,ALTER更手术。
harpo


2

基本上,这就是方法。我只是想知道您是否有使用“ 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”模式中的系统目录视图,实际上不是严格或直接的表。

马克


1
我开始使用EXEC,是因为有时当我将脚本发送给存在对象的客户端时,SQL Server会抱怨。也许这是迷信,但它没有任何伤害。
harpo

2

与两个原因相比,我更喜欢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小提琴

我们可以看到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小提琴

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


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

删除创建

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 !!!  

第一次执行:
DC1

添加索引并执行: 在此处输入图片说明

强制计划: 在此处输入图片说明 在此处输入图片说明

另一个执行: 在此处输入图片说明

之后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,我们失去了强制计划。


1

看起来已经有一段时间了:链接文字

对我来说典型的脚本:

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

3
我建议使用a)“ sys”模式,以及b)更集中的系统视图,例如用于表的sys.tables,用于触发器的sys.triggers等,而不是仅使用通用的“ sysobjects”(不久将弃用) )。
marc_s 2009年

1

我将根据上下文使用:我的初始构建脚本或主要的重构脚本将使用检查/删除/创建,纯维护脚本将使用alter。


0

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

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

0

您不应该放下任何物体。放置对象存在两个问题:

1)如果CREATE失败,则不再有对象。(您可以使用事务来避免这种情况,但要花很多样板代码)

2)如果您没有明确地重新创建对象,则会失去对该对象的许可。


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


0

就我先前的回答而言

另一个原因,我更喜欢CREATE-ALTERDROP-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

而且,如果您不了解或不进行检查,最终将导致无法启动的过程。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.