Ответ 1
Да, вот как выглядят все мои скрипты сборки. В конце я также установил разрешения.
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 предложил, официальное слово предполагает, что эта функция не является высоким приоритетом. Отсюда и вопрос.
Да, вот как выглядят все мои скрипты сборки. В конце я также установил разрешения.
В этой статье рассказывается о потере разрешений при удалении объекта на SQL-сервере.
Итак, вот подход, который сохраняет права доступа:
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, вы получите более старую версию объекта.
2009 год, и SQL Server не имеет CREATE OR ALTER/REPLACE.
Это 2016 год, и теперь в окончательной CREATE OR ALTER
SQL Server 2016 он имеет DIE (исключение, если оно существует) и CREATE OR ALTER
(представлен в пакете обновления 1 (SP1) 2016 года).
Принятие 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 может использоваться в объектах программируемости, таких как:
- ХРАНЕННЫЕ ПРОЦЕДУРЫ (в том числе скомпилированные в исходном виде)
- ФУНКЦИИ (Transact-SQL, включая нативно скомпилированный)
- TRIGGERS
- ПРОСМОТРЫ
Но не может быть использован в:
- Объекты, которые требуют хранения (таблицы, индексы и индексированные представления)
- Пользовательские функции CLR
- Устаревшие объекты программируемости (RULE и DEFAULT)
- Непрограммируемые объекты (такие как CREATE ASSEMBLY, CREATE TABLE или CREATE - SCHEMA). В этих объектах синтаксис CREATE и ALTER сильно отличается от синтаксиса и удобства использования.
Каждый раз, когда разработчик пишет IF EXISTS(...) DROP
, тюремный щенок сжимается. Вы должны точно знать, что в базе данных, а ваше обновление script должно делать CREATe или ALTER как соответствующие, исходя из текущей версии вашей схемы приложения: Version Control и вашей базы данных.
Мы столкнулись с ситуацией, когда нам нужно было обновить удаленный сайт, но у нас не было разрешений DROP. До сих пор мы использовали "DROP и CREATE script, встроенные в SSMS 2008 R2, но теперь нам нужно было изменить. Мы создали три шаблона, которые мы бросаем над соответствующими сценариями 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 либо добавят это в свои списки "Script ___ 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
Я использую 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 из-за того, как мы работаем с контролем источника и т.д.
Это в основном способ сделать это, да. Мне просто интересно, есть ли у вас конкретная причина использовать подход "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
- это действительно плохая практика и может оставить вашу БД в плохом состоянии, если объект не создан (24/7 db!), а также то, что другие плакаты имеют упоминается о разрешениях на nuking.
Редакторы, такие как Sublime, Atom и VS Code, позволят вам создавать фрагменты кода в качестве шаблонов для быстрого создания вашего скелета script. SQL 2016 теперь, наконец, поддерживает конструкцию DROP IF EXISTS
, но она по-прежнему приближается с неправильного направления - все это drop/create
вместо одноразового create
в далеком прошлом и alter
с этого момента. Кроме того, я попытался сделать мои заголовки такими короткими, которые могут работать, поэтому я не становлюсь более привлекательным, чем 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
Procs:
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
Похоже, что он выключен: текст ссылки
типичный script для меня:
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
Я буду использовать либо в зависимости от контекста: мои сценарии начальной сборки или основного рефакторинга будут использовать check/drop/create, чистые сценарии обслуживания используют alter.
Я предпочитаю подход CREATE-ALTER
(не синтаксис) по сравнению с DROP-CREATE
по двум причинам:
DROP-CREATE
вы должны их воссоздать) Пример 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
Как мы видим, 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
В этом случае 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 !!!
Добавление индекса и выполнение:
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
Добавление индекса и выполнение:
С Drop-Create мы потеряли принудительный план.
У меня есть шаблон, который позволяет несколько раз выполнять script без ошибок.
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) Вы теряете права на объект, если вы их явно не создаете.
Я предпочитаю создавать пустой объект в условии "если не существует", а затем использовать ALTER и писать для этого вспомогательные процедуры.
Год 2017, а SQL Server имеет CREATE ИЛИ ALTER
SQL Server 2016 SP1 и SQL Server vNext имеют новый оператор языка T-SQL - CREATE [OR ALTER] для:
Только для моего продлить предыдущий ответ.
Другая причина, по которой я предпочитаю подход CREATE-ALTER
over 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
И если вы не знаете об этом или не проверяете, что вы закончите процедуру, которая не запустится.