ストアドプロシージャを作成する前に存在するかどうかを確認する方法


282

クライアントが「データベース管理」機能を実行するたびに実行する必要がある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
いいえ、それは動作しません。それは、あなたが望むものではないと主張されているストアドプロシージャを作成するためです。私たちが見ることができることから、それはそれが終わった後にもそれを落とさないので、それは用語のすべての面で間違いなく保存されます。非ストアドプロシージャが必要な理由無関係ではありません
David Hedlund '15年

「非保存」手順とはどういう意味ですか?サンプルで行うのは、ストアドプロシージャを再作成することだけです。これはあなたの質問とどう関係していますか?
AakashM、2010年

わかりました。問題は、多くのクライアントが使用する巨大なSQLスクリプトがあり、クライアントが当社のソフトウェアが提供する「データベース管理」機能を実行するたびに徹底的に実行する必要があることです。そのため、これらのクライアントの一部は、スクリプトの実行時にプロシージャが既に格納されている場合とされていない場合があります。私はこれが愚かであることを知っています。実際にこの手順を保存しないでおく必要はありません。存在するかどうかを確認し、存在しない場合は作成できます。ただし、T-SQL構文をどれだけ曲げようとしても、常にエラーが発生します。
シェーパー2010年

スクリプトを実行するたびに、プロシージャの作成を再試行します(残念ながら、create procedure呼び出しを含め、すべてを同じ.sqlファイルでスクリプト化する必要があります)。IF NOT EXISTS THEN CREATEは、構文の制限により機能しません。私に何ができる?
シェーパー2010年

回答:


199

クエリを実行できる場所であればどこでも手続き型コードを実行できます。

後にすべてをコピーするだけ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


はい、これは本当ですが、クエリ内から呼び出すためのプロシージャ、udfs、ビューなどが保存されないため、すべてのプロシージャ機能が失われます。(申し訳ありませんが、それを編集しました。私の頭の中でX-)は理にかなっています)
Adriaan Stander

1
はい。ただし、他のプロシージャ内からプロシージャを呼び出すか、そのリターンをテーブルへの入力として使用できます。
Adriaan Stander、2010年

@astander:ストアドプロシージャから匿名コードを呼び出すこともできます。それらの出力をで使用するにはINSERTOPENROWSETまたはを使用する必要があります。OPENQUERYこれは匿名コードでも機能します。もちろん、匿名コードには欠点があります。たとえば、それは呼び出し元の権限でのみ実行されます。私のポイントは、それが可能であり、物事を行う好ましい方法ではないということです:)
Quassnoi '15年

「存在しない場合にのみプロシージャを作成する必要がある場合、コードは問題ありません。」それがまさに私が知りたかったことです。実際のスクリプトでSSMS Createを使用しようとしましたが、うまくいきませんでした。しかし、Quassnoiに感謝します。不明瞭な質問については申し訳ありません。
シェーパー2010年

2
動的SQLを使用しない場合は、CREATE PROCステートメントがバッチ内の唯一のステートメントである必要があるため、この方法で実装した場合、DROP / CREATEの周囲にトランザクションをラップすることはできません。DROP PROC呼び出しの後にGO(バッチ区切り)が必要です。
Shiv 2015年

449

これはすでに回答済みとしてマークされていることを理解していますが、以前は次のようにしていた:

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
  ....

手順を落とさないようにするだけです。


74
これが良いアイデアである理由についてのメモを追加するだけです。1)ドロップするとセキュリティ設定がクリアされます。2)このようにすると、何らかの理由で変更スクリプトが失敗した場合、spはドロップされません。
Ryan Guill、2013

10
これは正解です。問題のストアドプロシージャのGRANTSが失われるのを防ぎます。
Andy_Vulhop 2014年

7
このアプローチには、ストアドプロシージャが存在しない時点がないという大きなメリットがあります。更新が他の人、システム、またはスレッドによってまだ使用されている間に重要なシステムに適用されている場合、これは非常に重要です。ストアドプロシージャを一時的に削除することによって引き起こされるエラーを追跡することは、再現が非常に難しいため、非常に厄介な作業になる可能性があります。
James

3
これは、すでに述べた多くの理由から素晴らしい解決策です。DBAがprocメタデータ(created-dateなど)に依存している場合、これを追加したいのですが、procを作成するのではなく、そのままにします。毎回新品。これを自分のprocを維持するための私のチームの「ベストプラクティス」に変えようとしています。これは通常、いくつかのDBにコピー/伝播する必要があります。
NateJ 2015年

2
また、一部の人々変更された場合に備えてGRANT、スクリプト内でステートメントを明示的に必要とすることを考慮してください。したがって、のDROP代わりに使用する正当な理由がありますALTER
Cody Stott

123

削除する前にデータベースオブジェクトの存在を確認する最も簡単な方法を探している場合は、次の1つの方法を使用します(例では上記の例と同じようにSPROCを使用していますが、テーブルやインデックスなどを変更できます)。

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

これは迅速かつエレガントですが、すべてのオブジェクトタイプで一意のオブジェクト名を使用する必要があるため、オブジェクト名が一意であることを確認する必要があります。

これが役に立てば幸いです!


62
より良い:IF(OBJECT_ID( 'MyProcedure'、 'P')IS NOT NULL)DROP PROCEDURE MyProcedure GO
alerya

32

「プロシージャが存在する場合は変更し、存在しない場合は削除する」ことを望んでいますが、常にプロシージャを削除してから再作成する方が簡単だと思います。プロシージャがすでに存在する場合にのみ、プロシージャを削除する方法は次のとおりです。

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

2番目のパラメータは、ストアドプロシージャあるOBJECT_IDを使用してオブジェクトのみを検索するように指示します。object_type = 'P'

AF =集計関数(CLR)

C = CHECK制約

D = DEFAULT(制約またはスタンドアロン)

F = FOREIGN KEY制約

FN = SQLスカラー関数

FS =アセンブリ(CLR)スカラー関数

FT =アセンブリ(CLR)テーブル値関数

IF = SQLインラインテーブル値関数

IT =内部テーブル

P = SQLストアドプロシージャ

PC =アセンブリ(CLR)ストアドプロシージャ

PG =計画ガイド

PK = PRIMARY KEY制約

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ファイルでは失敗します。
ジェームズL.

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構文ではありません...投票が始まる前に回答を削除し、初心者の混乱を避けるためのアドバイスです。
Pawel Czapski、2018

@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 ...

DROP PROCEDUREのようなコードが1行だけの場合は、開始と終了は必要ありません...
Phillip Senn

警告:「ストアドプロシージャが存在するかどうかを確認する」関数は、どの関数名を入力しても(T-SQLの場合)、常に「存在する」を返します。信頼性の低いチェックです。
Ryan Battistone、2018

より良い代替策:IF EXISTS(SELECT 1 FROM sys.procedures WHERE name = 'name_of_table_as_seen_in_sysprocedures')BEGIN select -1 as 'status' END
Ryan Battistone

5

この方法を使用する方法とその理由を以下に示します。ストアドプロシージャを編集するのはそれほどきれいではありませんが、賛否両論があります...

更新:この呼び出し全体をTRANSACTIONでラップすることもできます。すべてをコミットまたはすべてロールバックできる単一のトランザクションに多くのストアドプロシージャを含める。トランザクションでラップするもう1つの利点は、READ UNCOMMITTEDトランザクション分離レベルを使用しない限り、他のSQL接続に対してストアドプロシージャが常に存在することです。

1)プロセスの決定と同じように変更を回避する。私たちのプロセスは、存在する場合は常に削除して作成します。新しいPROCが望ましいprocであると想定する同じパターンを実行する場合、IF EXISTS ALTER ELSE CREATEを使用することになるため、変更の提供は少し難しくなります。

2)動的SQLの外部のトランザクションで一連のプロシージャの更新をラップできないように、CREATE / ALTERをバッチの最初の呼び出しとして配置する必要があります。基本的に、手順の更新のスタック全体を実行するか、DBバックアップを復元せずにすべてをロールバックする場合、これはすべてを1つのバッチで実行する方法です。

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を使用したGeoffの答え(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

以下のリンクをクリックして、トリガーと機能のIF存在を確認します。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

..........


ここでLIKE%ステートメントを使用するのは悪い考えです。OPがuspBlackListGetAll_V2など、ドロップしたくない別のsprocを持っている場合はどうなりますか?
Dave Hogan

@DaveHogan同意する。ただし、彼はを付けなかった%ため、LIKEとして動作します=
Diego Jancic

1
@DiegoJancic編集した履歴を見ると、元々「%」が付いていたことがわかります
Dave Hogan

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

私は最初の2つのプロシージャが既に存在していることをすでに知っていますSQLはクエリを実行し、最初の2つのプロシージャのエラーを出しますが、それでも最後のプロシージャが作成されますクライアント!


-2

CREATE Procedure IF NOT EXISTS 'Your proc-name'()BEGIN ... END


プロシージャが存在する場合、これは何もしません。リクエスタは、プロシージャが存在する場合はそれを変更し、存在しない場合は作成することを望みます。
Randy Gamage、2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.