Как стать автором
Обновить

Комментарии 14

Еще одно замечание, почему объекты надо альтерить, а не дропать — в случае дропа у вас есть период, когда объект не существует, но он может вызываться из работающего кода, и код этот получит ошибку отсутствия объекта. Случай маловероятный, но неприятный.

ну и по вашей процедуре замечания:

1) Примечательно, что ее вы дропаете и создаете вместо использования аналогичного метода :)
2) Для процедуры достаточно такого определения:
EXEC('CREATE PROC [procName] AS');

3) с IF, TF нужна получше обработка. Пример: у вас уже есть инлайновая табличная функция, вы переделываете ее в мультистейтмент. Своей процедуре вы передаете TF, но при этом внутри вы должны проверить и существование IF, и существование TF, иначе вы получите ошибку либо при первом, либо при повторном запуске. альтерить IF в TF и обратно можно, но проверять нужно оба варианта
4) Вы вроде для Type и для Name забыли кавычки внутри сгенерированного кода.
5) Внутри сгенерированного кода не надо писать GO — это разделитель между батчами в студии, а не инструкция SQL.
6) Ну это мелочь — перед юникодной строкой надо ставить N.
7) Вообще непонятно, зачем вам 4 переменных, и вы выбираете, какую из них выполнить. Можно же сильно все упростить.

По итогу я бы превратил вашу процедуру в что-то такое:

IF OBJECT_ID('dbo.antidrop', 'P') IS NOT NULL
    DROP PROC dbo.antidrop;
GO
CREATE PROC dbo.antidrop @name SYSNAME, @type SYSNAME
AS
BEGIN

	declare @sql nvarchar(max);

	select @sql =	N'if ' + 
					case when @type in ('IF', 'TF') then N'OBJECT_ID(''' + @name + N''', ''IF'') is null and OBJECT_ID(''' + @name + N''', ''TF'') is null' 
													else N'OBJECT_ID(''' + @name + N''', ''' + @type + N''') IS NULL'
							end + N'
	EXEC(''CREATE ' + 
					case @type	when 'IF' then N'FUNCTION'
								when 'TF' then N'FUNCTION'
								when 'FN' then N'FUNCTION'
								when 'P' then N'PROC'
								when 'V' then N'VIEW'
							end + 
					N' ' + @name + 
					case @type	when 'IF' then N'() RETURNS table as return (select 1 as i)'
								when 'TF' then N'() RETURNS table as return (select 1 as i)'
								when 'FN' then N'(@i INT) RETURNS INT AS begin return 1; end'
								when 'P' then N' as'
								when 'V' then N' AS SELECT 1 AS i'
							end + 
					';'');';

	exec(@sql);

END
GO

Алексей, спасибо за развернутый комментарий.
1. Исправил
2. Исправил
3. dbo.antidrop не планировалась как готовое решение, лишь пример на коленке
4. Заметил еще до модерации, но редактировать было нельзя…
5. Исправил

По поводу 4х непонятнных переменных, хотелось чтобы суть была ясна начинающим разработчикам в том числе. Безусловно были динамические мысли. В общем было принято решение оставить более простенький вариант.
в случае дропа у вас есть период, когда объект не существует, но он может вызываться из работающего кода

А это вообще нормальная практика — менять базу на продакшене прямо под нагрузкой? Я не DBA, но почему-то считал, что у DBA есть процедуры деплоя изменений, гарантированно исключающие шаловливые ручонки на работающем продакшене.
Ну как бы есть базы без технологических перерывов, работающие 24/7, и на них тоже надо устанавливать обновления, не останавливая работу. Для этого используются различные интересные техники. Особенно интересно обновлять высоконагруженные таблицы — например, добавлять туда колонки и заполнять их значениями. Изменение процедуры — это так — разумная предосторожность.
Ну вот собственно мне и представлялось, что в таких системах 24/7 всегда есть стейджинг и/или горячий резерв, которые не несут основной нарузки, и что на них собственно и накатываются все изменения без опасности прогнуть или поломать продакшен, с возможностью протестировать изменения на малой аудитории, после чего перекинуть нагрузку через балансировщик.
Так это же база данных. Она одна на весь продакшен, поэтому и изменения надо накатывать сразу все.
Люблю синтаксис «CREATE OR REPLACE», чего и MS рекомендую. Дешево и сердито.

Ms давно (то есть недавно) в курсе, а вот потребители — увы… Я до сих пор вынужден поддержиавать sql 2000 (две тысячи ровно). И я впервые увидел эмуляцию create or alter, без дропа и без обработки ошибок на клиенте. Это так просто! Где были мои глаза?!!

Мне приходится иногда разбираться с Oracle 8 и тут уже тяжело сказать кто из них "круче".
По поводу самой техники создания болванки и ее изменения — довольно известная вещь в среде SQL Server.
Крайне рекомендую ознакомиться с отличной статьей https://habrahabr.ru/post/315142/ и просто шедевральной серией статьей Bad Habits Revival

В общем Константин (kast218) уже ответил. Действительно данный функционал уже существует, но только начиная с версии SQL Server 2016
CREATE OR ALTER
Для таблиц есть интересный приём. Когда нужно целиком перетащить таблицу с одного сервера на другой. И при этом на целевом сервере таблица уже есть, и структура может отличаться.
сначала тащим таблицу insert into table_new, если стащили успешно преименовываем и дропаем старую.
Я обычно полагаю, что разработчики пишут скрипты не с широко закрытыми глазами, поэтому сначала должен быть запущен скрипт на создание, на alter — когда-нибудь потом.
Соответственно, в создающем скрипте проверка на существование может быть, но только для того, чтобы он был пере-запускаемым (это удобно при активной разработке). Точно по тем же причинам, в скрипте с alter существование таблицы не стоит проверять, а вот добавляемых столбцов — может быть полезно. Естественно, в создающем скрипте раздаются права, если это требуется.

Если кто-то запустит скрипт на alter до создающего скрипта — желательно чтобы он упал, потому что, как следствие, стоит исправить что-то в процессе разработки или мыслительных процессах конкретного разработчика :)

P.S. SQL Ninja, в контексте статьи — это кто? Они под покровом ночи делают незаметные для других изменения в БД? :)
if object_id(N'SmthProc', N'p') is not null
	set noexec on;
go

create procedure SmthProc
as
	set nocount on;
go

set noexec off;
go

alter procedure SmthProc
as
begin
	set nocount on

	print N'SmthProc'
end
go


а так?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации