你在SQL Server中做什么来创build或修改?
今年是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
对于触发器,您必须依靠专有系统视图。
这是最受欢迎的习俗吗?
编辑:正如n8wrlbuild议, 官方的话表明,这个function不是一个高优先级。 因此,这个问题。
是的,这就是我所有的构build脚本的样子。 最后,我还设置了权限。
本文在删除SQL Server中的对象时会丢失权限。
- 提示'N'技巧 – T-SQL – 创build或更改存储过程的一种方法
所以这里是保留权限的方法:
IF OBJECT_ID('spCallSomething') IS NULL EXEC('CREATE PROCEDURE spCallSomething AS SET NOCOUNT ON;') GO ALTER PROCEDURE spCallSomething ... --instead of DROP/CREATE
也适用于函数,只需用上面代码中的FUNCTION
代替PROCEDURE
。
考虑这样做的另一个原因是容忍失败。 假设你的DROP成功了,但是你的CREATE失败 – 你以一个破损的DB结束。 使用ALTER方法,你将会得到一个老版本的对象。
今年是2009年,SQL Server没有CREATE OR ALTER / REPLACE。
今年是2016年,现在在SQL Server 2016 RTM中有DIE( Drop If Exists ),而CREATE OR ALTER
(在2016 SP1中引入)。
采取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
相应的MSSQL Tiger Team博客文章解释道
CREATE OR ALTER可用于可编程性对象,例如:
- stored procedures(包括本地编译)
- function(Transact-SQL,包括本机编译)
- TRIGGERS
- VIEWS
但不能用于:
- 需要存储的对象(表,索引和索引视图)
- CLR用户定义的函数
- 弃用的可编程性对象(RULE和DEFAULT)
- 非可编程性对象(如CREATE ASSEMBLY,CREATE TABLE或CREATE-SCHEMA)。 在这些对象上,CREATE和ALTER的语法与语法和可用性透视图非常不同。
每次开发者写入IF EXISTS(...) DROP
一个印章的小狗被杵在一起。 您应该确切地知道数据库中的内容,并且升级脚本应根据应用程序模式的当前版本(适用于版本控制和数据库)进行适当的CREATe或ALTER升级。
我们遇到了需要更新远程站点的情况,但是我们没有DROP权限。 到目前为止,我们一直在使用SSMS 2008 R2内置的“DROP CREATE”脚本,但现在我们需要改变。 我们创build了三个模板,当我们需要更新存储过程或函数时,我们将其放在适当的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不会更改它,如果它们添加或修改权限,它们将保留。 这样做,复制函数或存储过程的名称是一件容易的事情,并使用模板参数replace来自动完成这些脚本。
现在,我希望微软的好人可以将它添加到他们的“Script ___ as”列表中,或者让我们能够创build自己的脚本,
您可能想要在SQL Server反馈条目后面添加一些权限: https : //connect.microsoft.com/SQLServer/feedback/details/344991/create-or-alter-statement 。 它似乎是less数仍然可以公开访问的国家之一,他们表示,“已经开始对此进行可行性审查,以决定我们是否可以在不久的将来发布这个报告”。 声音越多,发生的可能性就越大!
(更新:现在也使用下面的代码触发器和视图)
-- 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
我会在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是因为我们如何处理源代码控制等
基本上是这样做的,是的。 我只是想知道你是否有特殊的理由来使用“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”模式下的系统目录视图 – 不是严格的或直接的表。
渣子
我总是alter
我的对象,因为drop
是非常糟糕的做法,如果一个对象无法创build(24/7分贝!),以及其他海报提到的关于nuking权限的问题,可能会使数据库处于不良状态。
像Sublime,Atom和VS Code这样的编辑器可以让你制作代码片段作为模板,以便快速创build你的骨架脚本。 SQL 2016现在终于支持DROP IF EXISTS
构造了,但是它仍然从错误的方向接近 – 所有东西都是一个drop/create
而不是在遥远的过去create
一次,然后alter
。 此外,我试图使我的标题尽可能短的工作,所以我没有比create proc dbo.myproc as
create
存根( create proc dbo.myproc as
。
浏览次数:
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
看起来像是一段时间了: 链接文本
我的典型脚本:
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
我将根据上下文使用:初始构build或主要重构脚本将使用check / drop / create,纯维护脚本使用alter。
我有一个模板,它允许多次执行脚本没有错误。
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
你不应该放弃一个对象。 丢弃物体有两个问题:
1)如果CREATE失败,则不再有对象。 (您可以使用交易来避免这种情况,代价是很多样板代码)
2)如果您没有明确地重新创build对象,则会丢失对象的权限。
我更喜欢在“如果不存在”的条件下创build一个空白对象,然后使用ALTER,并为此写了一些帮助程序。
今年是2017年,SQL Server有CREATE OR ALTER
SQL Server 2016 SP1和SQL Server vNext具有新的T-SQL语言语句 – CREATE [OR ALTER] for:
- STOREDPROCEDURES
- function
- TRIGGERS
- VIEWS
我更喜欢CREATE-ALTER
方法(不是语法),而不是DROP-CREATE
,原因有二:
- 权限(使用
DROP-CREATE
你必须重新创build它们) - 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查询存储并强制为存储过程使用特定的查询计划。
DROP-CREATE
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
:
创build – 修改
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
:
结果
随着下降创build我们失去了强制计划。
只是对我以前的答复 。
我更喜欢使用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
如果你没有意识到这一点,或者你不检查,最终的程序将不会启动。