Как проверить, существует ли хранимая процедура перед ее созданием

284

У меня есть сценарий SQL, который должен запускаться каждый раз, когда клиент выполняет функцию «управления базой данных». Сценарий включает в себя создание хранимых процедур в клиентской базе данных. Некоторые из этих клиентов могут уже иметь хранимую процедуру при запуске сценария, а некоторые - нет. Мне нужно добавить отсутствующие хранимые процедуры в клиентскую базу данных, но не важно, насколько я пытаюсь изменить синтаксис T-SQL, я получаю

CREATE / ALTER PROCEDURE 'должен быть первым оператором в пакете запроса

Я прочитал об этом перед созданием работ, но мне не нравится делать это таким образом.

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'MyProc')
DROP PROCEDURE MyProc
GO

CREATE PROCEDURE MyProc
...

Как добавить проверку на наличие хранимой процедуры и создать ее, если она не существует, но изменить ее, если она существует?

Формирователь
источник
2
Нет, это не работает, потому что это создает хранимую процедуру, которая якобы не то, что вы хотите. из того, что мы можем видеть, оно не отбрасывает его и после того, как оно сделано, поэтому оно определенно хранится во всех аспектах термина. это не имеет значения , почему вам нужна процедура нон сохраненными
Дэвид Хедлунд
Что вы подразумеваете под «не хранимой» процедурой? Все, что делает ваш пример - это воссоздает хранимую процедуру; какое это имеет отношение к вашему вопросу?
AakashM
Хорошо, поехали. Дело в том, что у меня есть ОГРОМНЫЙ сценарий SQL, который используют многие клиенты, и его нужно тщательно запускать каждый раз, когда клиент выполняет функцию «управления базами данных», которую предоставляет наше программное обеспечение. Таким образом, некоторые из этих клиентов могут уже хранить процедуру при запуске сценария, а некоторые - нет. Я знаю, что это глупо, мне на самом деле не нужна эта процедура, чтобы остаться незарегистрированной, я могу просто проверить, существует ли она, и создать ее, если ее нет. Тем не менее, не имеет значения, сколько я пытаюсь изогнуть синтаксис T-SQL, всегда есть ошибка.
Формирователь
Каждый раз, когда они запускают скрипт, он будет пытаться снова создать процедуру (к сожалению, все должно быть записано в одном и том же файле .sql, включая вызов процедуры create). Если не существует, то CREATE не работает из-за синтаксических ограничений. Что я могу сделать?
Формирователь
3
Возможный дубликат stackoverflow.com/questions/937908/…
Майкл Фрейдгейм

Ответы:

200

Вы можете запускать процедурный код везде, где можете выполнить запрос.

Просто скопируйте все после AS:

BEGIN
    DECLARE @myvar INT
    SELECT  *
    FROM    mytable
    WHERE   @myvar ...
END

Этот код выполняет те же действия, что и хранимый процесс, но не хранится на стороне базы данных.

Это очень похоже на то, что называется анонимной процедурой в PL/SQL.

Обновить:

Название вашего вопроса немного сбивает с толку.

Если вам нужно только создать процедуру, если она не существует, тогда ваш код в порядке.

Вот что SSMSвыводится в скрипте создания:

IF EXISTS ( SELECT  *
            FROM    sys.objects
            WHERE   object_id = OBJECT_ID(N'myproc')
                    AND type IN ( N'P', N'PC' ) ) 
DROP 
CREATE 

Обновить:

Пример того, как это сделать при включении схемы:

IF EXISTS ( SELECT * 
            FROM   sysobjects 
            WHERE  id = object_id(N'[dbo].[MyProc]') 
                   and OBJECTPROPERTY(id, N'IsProcedure') = 1 )
BEGIN
    DROP PROCEDURE [dbo].[MyProc]
END

В приведенном выше примере dbo - это схема.

Обновить:

В SQL Server 2016+ вы можете просто

CREATE OR ALTER PROCEDURE dbo.MyProc

Quassnoi
источник
Да, это правда, но вы потеряете все процедурные функции, так как никакие процедуры, udfs, представления и тому подобное не будут сохраняться для вызова из запросов. (Извините, отредактировал это, это имело смысл в моей голове X-))
Adriaan Stander
1
Да, но вы можете вызывать процедуры из других процедур или использовать их возврат в качестве входных данных для таблицы.
Адриан Стандер
@astander: вы также можете вызвать анонимный код из хранимых процедур. Чтобы использовать их вывод в an INSERT, вам нужно использовать OPENROWSETили OPENQUERYкоторый работает с анонимным кодом. Конечно, есть недостатки в анонимном коде: например, он работает только под привилегиями вызывающего. Моя точка зрения заключается в том, что это возможно, а не предпочитаемый способ ведения дел :)
Quassnoi
«Если вам нужно только создать процедуру, если она не существует, то ваш код просто в порядке». И это именно то, что я хотел знать. Я пытался использовать SSMS Create для реального сценария, но это не помогло. Но спасибо Quassnoi, и я извиняюсь за неясный вопрос.
Формирователь
2
Оператор CREATE PROC должен быть единственным оператором в пакете, когда не используется динамический SQL, поэтому вы не можете обернуть транзакцию вокруг DROP / CREATE при реализации таким способом. После вызова DROP PROC должен быть GO (разделитель пакетов).
Шив
450

Я понимаю, что это уже помечено как ответ, но мы делали это так:

IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.MyProc'))
   exec('CREATE PROCEDURE [dbo].[MyProc] AS BEGIN SET NOCOUNT ON; END')
GO

ALTER PROCEDURE [dbo].[MyProc] 
AS
  ....

Просто чтобы не уронить процедуру.

Geoff
источник
74
Просто добавьте некоторые примечания о том, почему это хорошая идея: 1) перетаскивание очистит все настройки безопасности, 2) сделав это таким образом, если сценарий alter по какой-то причине завершится неудачно, sp не будет удален.
Райан Гилл
10
Это действительно правильный ответ. Это позволяет избежать потери каких-либо грантов для хранимого процесса.
Andy_Vulhop
7
Этот подход имеет огромное преимущество в том, что не существует момента времени, когда хранимая процедура не существует. Это может иметь решающее значение, если обновление применяется к критической системе, пока оно все еще используется другими людьми, системами или потоками. Отслеживание ошибок, вызванных мгновенным удалением хранимой процедуры, может быть довольно неприятным, поскольку их очень трудно воспроизвести.
Джеймс
3
Это отличное решение по многим причинам, которые уже упоминались, и я просто хотел бы добавить, что, если администраторы базируются на метаданных proc (таких как дата создания), это оставляет эти вещи нетронутыми, вместо того, чтобы делать proc Совершенно новый каждый раз. Я пытаюсь превратить это в «лучшую практику» моей команды для поддержки наших собственных процедур, которые обычно нужно копировать / распространять на несколько БД.
NateJ
2
Также считаю , что некоторые люди хотят в GRANTявной форме заявления , содержащиеся в сценарии в случае , если они изменить ; так что все еще есть оправдание для использования DROPвместо ALTER.
Коди Стотт
123

Если вы ищете самый простой способ проверить существование объекта базы данных перед его удалением, вот один из способов (в примере используется SPROC, как и в предыдущем примере, но его можно изменить для таблиц, индексов и т. Д.):

IF (OBJECT_ID('MyProcedure') IS NOT NULL)
  DROP PROCEDURE MyProcedure
GO

Это быстро и элегантно, но вы должны убедиться, что у вас есть уникальные имена объектов для всех типов объектов, так как это не учитывает это.

Надеюсь, это поможет!

MrChips
источник
62
Что лучше: ЕСЛИ (OBJECT_ID ('MyProcedure', 'P') НЕ НУЖНО) ПРОЦЕДУРА УДАЛЕНИЯ MyProcedure GO
alerya
33

Я знаю, что вы хотите «изменить процедуру, если она существует, и удалять ее только в том случае, если она не существует», но я считаю, что проще всегда просто отбросить процедуру, а затем воссоздать ее. Вот как можно отбросить процедуру, только если она уже существует:

IF OBJECT_ID('MyProcedure', 'P') IS NOT NULL
    DROP PROCEDURE MyProcedure
GO

Второй параметр указывает OBJECT_IDтолько искать объекты с object_type = 'P', которые хранимых процедур:

AF = Агрегатная функция (CLR)

C = CHECK ограничение

D = DEFAULT (ограничение или автономно)

F = ограничение КЛЮЧЕВОЙ КЛЮЧ

FN = скалярная функция SQL

Скалярная функция FS = Assembly (CLR)

FT = сборочная (CLR) табличная функция

IF = встроенная табличная функция SQL

IT = Внутренняя таблица

P = хранимая процедура SQL

ПК = сборка (CLR) хранимая процедура

PG = План руководства

PK = ограничение первичного ключа

R = правило (в старом стиле, автономное)

RF = процедура репликации фильтра

S = базовая таблица системы

SN = Синоним

SO = объект последовательности

TF = табличная функция SQL

TR = триггер

Вы можете получить полный список опций через:

SELECT name 
FROM master..spt_values
WHERE type = 'O9T'
Майкл Керри
источник
1
TF отсутствует. Тем не менее, +1 для предоставления этого списка
Crono
Также TR для запуска
CarlosOro
23

Я знаю, что это очень старая публикация, но поскольку она появляется в верхних результатах поиска, следовательно, добавляется последнее обновление для тех, кто использует SQL Server 2016 SP1 -

create or alter procedure procTest
as
begin
 print (1)
end;
go

Это создает хранимую процедуру, если она еще не существует, но изменяет ее, если она существует.

Ссылка

ГКБ
источник
1
Это так, так полезно.
AgentFire
Я хочу подчеркнуть, что это работает только в SQL Studio - в файле sql мне это не удается.
Джеймс Л.
10

DROP IF EXISTS - это новая функция SQL Server 2016

https://blogs.msdn.microsoft.com/sqlserverstorageengine/2015/11/03/drop-if-exists-new-thing-in-sql-server-2016/

DROP  PROCEDURE IF EXISTS dbo.[procname]
Джей Джей
источник
1
это не синтаксис SqlServer ... совет убрать ответ, прежде чем парни начнут понизить голосование и избежать путаницы для новичков.
Павел Чапски
@PawelCz действительно для SQL Server 2016 и выше, я перефразировал ответ. Спасибо за ответ!
JayJay
Это не отвечает оригинальному сообщению. Существует тонкая разница между автоматическим отбрасыванием и воссозданием и созданием, только если его не существует. Удаление процесса приведет к снижению безопасности, связанной с ним, что, возможно, было записано в сценарии.
Рон
7

У меня была такая же ошибка. Я знаю, что этот поток уже практически мертв, но я хочу установить другую опцию, кроме "анонимной процедуры".

Я решил это так:

  1. Проверьте, существует ли хранимая процедура:

    IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='my_procedure') BEGIN
        print 'exists'  -- or watever you want
    END ELSE BEGIN
        print 'doesn''texists'   -- or watever you want
    END
  2. Однако "CREATE/ALTER PROCEDURE' must be the first statement in a query batch"это все еще там. Я решил это так:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE -- view procedure function or anything you want ...
  3. Я в конечном итоге с этим кодом:

    IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID('my_procedure'))
    BEGIN
        DROP PROCEDURE my_procedure
    END
    
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE PROCEDURE [dbo].my_procedure ...
Oaxas
источник
Вам не нужно начинать и заканчивать, если это всего лишь 1 строка кода, например, DROP PROCEDURE ...
Phillip Senn
Предупреждение: функция «проверьте, существует ли хранимая процедура» всегда будет возвращать «существует», независимо от того, какое имя функции вы указали (для T-SQL). Это ненадежная проверка.
Райан Баттистон
Лучшая альтернатива: ЕСЛИ СУЩЕСТВУЕТ (ВЫБЕРИТЕ 1 ИЗ sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures') НАЧАТЬ выбрать -1 в качестве 'status' END
Райан Баттистон
5

Вот метод и некоторые аргументы за его использование. Редактировать сохраненный процесс не так приятно, но есть плюсы и минусы ...

ОБНОВЛЕНИЕ: Вы можете также обернуть весь этот вызов в СДЕЛКУ. Включая множество хранимых процедур в одну транзакцию, которая может зафиксировать или откатить все. Еще одним преимуществом переноса транзакции является то, что хранимая процедура всегда существует для других соединений SQL, если они не используют уровень изоляции транзакции READ UNCOMMITTED!

1) Избегать изменений просто как процесс принятия решения. Наши процессы должны всегда, ЕСЛИ СУЩЕСТВУЕТ, ТО, ЧЕМ СОЗДАТЬ. Если вы сделаете то же самое, предполагая, что новый PROC является желаемым процессом, обслуживание изменений будет немного сложнее, потому что у вас будет IF EXISTS ALTER ELSE CREATE.

2) Вы должны поместить CREATE / ALTER в качестве первого вызова в пакете, чтобы вы не могли обернуть последовательность обновлений процедур в транзакции вне динамического SQL. В основном, если вы хотите запустить целый стек обновлений процедур или откатить их все назад, не восстанавливая резервную копию БД, это способ сделать все в одном пакете.

IF NOT EXISTS (select ss.name as SchemaName, sp.name as StoredProc 
    from sys.procedures sp
    join sys.schemas ss on sp.schema_id = ss.schema_id
    where ss.name = 'dbo' and sp.name = 'MyStoredProc')
BEGIN
    DECLARE @sql NVARCHAR(MAX)

    -- Not so aesthetically pleasing part. The actual proc definition is stored
    -- in our variable and then executed.
    SELECT @sql = 'CREATE PROCEDURE [dbo].[MyStoredProc]
(
@MyParam int
)
AS
SELECT @MyParam'
    EXEC sp_executesql @sql
END
Шив
источник
5

В SQL Server 2008 года вы можете использовать " INFORMATION_SCHEMA.ROUTINES"

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES 
  WHERE ROUTINE_NAME = 'MySP'
        AND ROUTINE_TYPE = 'PROCEDURE') 
Ромил Кумар Джайн
источник
3

Очевидно, у меня нет репутации, необходимой для голосования или комментирования, но я просто хотел сказать, что ответ Джеффа с использованием EXEC (sp_executesql может быть лучше) определенно является подходящим вариантом. Удаление и последующее воссоздание хранимой процедуры завершает работу, но есть момент, когда хранимая процедура вообще не существует, и это может быть очень плохо, особенно если это что-то, что будет бегать многократно. У меня были всевозможные проблемы с моим приложением, потому что фоновый поток выполнял IF EXISTS DROP ... CREATE в то же время, когда другой поток пытался использовать хранимую процедуру.

Джеймс
источник
3

** Самый простой способ удалить и воссоздать сохраненный процесс в T-Sql это **

Use DatabaseName
go
If Object_Id('schema.storedprocname') is not null
begin
   drop procedure schema.storedprocname
end
go

create procedure schema.storedprocname
as

begin
end
Ренниш Джозеф
источник
3

Вот сценарий, который я использую. Благодаря этому я избегаю ненужного сброса и воссоздания сохраненных процедур.

IF NOT EXISTS (
    SELECT *
    FROM sys.objects
    WHERE object_id = OBJECT_ID(N'[dbo].[uspMyProcedure]')
    )
BEGIN
  EXEC sp_executesql N'CREATE PROCEDURE [dbo].[uspMyProcedure] AS select 1'
END
GO

ALTER PROCEDURE [dbo].[uspMyProcedure] 
    @variable1 INTEGER  
AS
BEGIN
   -- Stored procedure logic
END
Мирослава
источник
2

Проверьте, если существует для хранимой процедуры

IF EXISTS (SELECT * FROM sys.objects 
            WHERE object_id = OBJECT_ID
             (N'[Schema].[Procedure_Name]') AND type IN (N'P', N'PC'))
BEGIN
       DROP PROCEDURE [Schema].[Procedure_Name]
       Print('Proceudre dropped => [Schema].[Procedure_Name]')
END

Проверьте, если существует для триггера, функция также нажав ссылку ниже http://www.gurujipoint.com/2017/05/check-if-exist-for-trigger-function-and.html

Джатин Фулера
источник
1

почему бы тебе не пойти простым путем, как

    IF EXISTS(SELECT * FROM sys.procedures WHERE NAME LIKE 'uspBlackListGetAll')
    BEGIN
         DROP PROCEDURE uspBlackListGetAll
    END
    GO

    CREATE Procedure uspBlackListGetAll

..........

dnxit
источник
Плохая идея использовать здесь LIKE%. Что, если у ОП был другой спрок, такой как uspBlackListGetAll_V2, который они не хотели отбрасывать?
Дэйв Хоган
@DaveHogan Я согласен. Однако он не положил %, поэтому LIKEведет себя как=
Диего Янчич
1
@DiegoJancic Если вы посмотрите на отредактированную историю, вы увидите, что она изначально была с «%»
Дейв Хоган,
0

В дополнение к ответу @Geoff я создал простой инструмент, который генерирует SQL-файл, который содержит операторы для хранимых процедур, представлений, функций и триггеров.

Смотрите MyDbUtils @ CodePlex . введите описание изображения здесь

Стеф Хейенрат
источник
1
Я думаю, что Management Studio уже предоставляет такой инструмент. Это называется «Генерация сценариев»
Hybris95,
0

Я думаю! Почему я не пишу весь запрос, как

GO
create procedure [dbo].[spAddNewClass] @ClassName varchar(20),@ClassFee int
as
begin
insert into tblClass values (@ClassName,@ClassFee)
end

GO
create procedure [dbo].[spAddNewSection] @SectionName varchar(20),@ClassID       int
as
begin
insert into tblSection values(@SectionName,@ClassID)
end

Go
create procedure test
as
begin 
select * from tblstudent
end

я уже знаю, что первые две процедуры уже существуют, sql выполнит запрос, выдаст ошибку первых двух процедур, но все же он создаст последнюю процедуру, SQl сама заботится о том, что уже существует, это то, что я всегда делаю со всеми своими клиенты!

Шейх Номан Насир
источник
-2

CREATE Процедура, ЕСЛИ НЕ СУЩЕСТВУЕТ 'Ваше имя-процесса' () НАЧИНАЕТСЯ ... КОНЕЦ

Wartari
источник
это ничего не сделает, если процедура существует. Запрашивающая сторона хочет изменить процедуру, если она существует, создать ее, если нет.
Рэнди Гэмэйдж