你在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
如果你没有意识到这一点,或者你不检查,最终的程序将不会启动。