存在しない場合は、コードによって新しい関数を作成します


15

データベースにスクリプトで新しい関数を作成したい。スクリプトコードは次のとおりです。

IF Exists(Select * From sys.sysobjects A Where A.name =N'fn_myfunc' and xtype=N'FN') return;

CREATE FUNCTION fn_myfunc ()
returns varchar(10)
AS Begin
...
End

しかし、上記のスクリプトを実行すると、SQL Serverはエラーを返します。

'CREATE FUNCTION' must be the first statement in a query batch.

回答:


16

2017年1月更新-SQL Server 2016+ / Azure SQL Database

SQL Server 2016および現在のバージョンのAzure SQL Databaseには、関数、プロシージャ、テーブル、データベースなどの次の構文があります。(DROP IF EXISTS):

DROP FUNCTION IF EXISTS dbo.fn_myfunc;

また、SQL Server 2016 Service Pack 1では、モジュール(関数、プロシージャ、トリガー、ビュー)にさらに優れた機能が追加され、アクセス許可や依存関係が失われないことを意味します(CREATE OR ALTER)。

CREATE OR ALTER FUNCTION dbo.fn_myfunc ...

これらの両方の構文の強化により、ソース管理、展開などに使用されるはるかに単純なスクリプトを作成できます。

しかし、使用している場合...


古いバージョン

Management Studioからこれをスクリプト化するとき、SQL Serverが行うことを行う必要があります。

IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE type = 'FN' AND name = 'fn_myfunc')
BEGIN
    DECLARE @sql NVARCHAR(MAX);
    SET @sql = N'CREATE FUNCTION ...';
    EXEC sp_executesql @sql;
END

または、あなたは言うことができます:

BEGIN TRY
    DROP FUNCTION dbo.fn_myfunc;
END TRY
BEGIN CATCH
    PRINT 'Function did not exist.';
END CATCH
GO
CREATE FUNCTION...

または、単に言うことができます:

DROP FUNCTION dbo.fn_myfunc;
GO
CREATE FUNCTION...

(関数が存在しない場合はエラーメッセージが表示されますが、スクリプトは次のGOから続行されるため、ドロップが機能したかどうかに関係なく、関数は(再)作成されます。)

関数を削除して再作成すると、権限と潜在的に依存関係の情報も失われることに注意してください。


1
キーワード$$ CREATE OR REPLACE $$はありませんか?残念。
12

@zinkingいいえ、しかし将来のバージョンではそうかもしれません。してください投票/コメント:connect.microsoft.com/SQLServer/feedback/details/127219/...
アーロン・ベルトラン

1
@AaronBertrand投票することをお勧めしますが、リンクが壊れています
青みがかっています

@bluishええ、リンクを投稿した3年前から今までにアイテムを削除しました... connect.microsoft.com/SQLServer/feedback/details/344991/…またはconnect.microsoft.com/SQLServer/feedbackをお
アーロンバートランド


1

エラーは一目瞭然です。修正するにはいくつかの方法があります。

  1. GO擬似キーワード、およびDROP/ CREATEオブジェクトを使用して、Management Studioでスクリプトを異なるバッチに分離します。(キーワード自体はManagement Studioオプションで変更できますが、これは事実上の設定なので、そのままにしておくことをお勧めします)。

    スクリプト(またはスクリプトの選択された部分)を実行すると、Management StudioはGOsの間でスクリプトの各チャンクを分離し、パーツを個別のバッチとしてSQL Serverに順次送信します。

  2. 動的SQLを使用して、別のバッチ内から個別のバッチを送信します。

    これは、スクリプトが外部機能に依存せずに正しく実行されるため、推奨される方法です。たとえば、アプリケーションにデータベース更新プログラムがある場合、一般的に言えば、スクリプトファイルをロードしてからターゲットサーバーで実行します。Management Studioと同様にバッチを分離するロジックを追加する必要があります(注:危険を伴う)、またはスクリプト全体を単一のバッチとして正常に実行できるようにスクリプトを記述します。

    別の回答で述べたように、CREATEこのメソッド(またはDROP/ CREATEなどの他の組み合わせ)を使用してtest / を実行できます。私がやりたいのは、オブジェクトが存在しない場合にスタブオブジェクトを作成し、それを使用ALTER <object>して実際に作成または変更を行うことです。この方法では、アクセス許可や拡張プロパティなどの依存関係は削除されません。また、単一のステートメントでCREATE/ を実行するためにエラーが発生しやすいロジックをコピー/貼り付けする必要はありませんALTER

    これが、スカラー関数の作成または変更に使用するテンプレートです。これを読者が他の種類のオブジェクト(ストアドプロシージャ、トリガーなど)に適応させるための演習として残しておきます。

IF NOT EXISTS(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[<schema>].[<function name>]') AND type IN ('FN', 'FS'))
    EXEC sp_executesql N'CREATE FUNCTION [<schema name>].[<function name>] (@a int) RETURNS int AS BEGIN /* Stub */ RETURN @a END'

EXEC sp_executesql N'
ALTER FUNCTION [<schema name>].[<function name>]
/* ... */
'

ここでオプション1はどのように機能しますか?を追加GOすると、スクリプトが破損することが保証されますIF
アーロンバートランド

@Aaron:DROP/ CREATEシナリオを考えていた-編集済み。ありがとう。
ジョンセイゲル

1

オブジェクトが存在するかどうかを確認し、存在しdatabaseない場合は作成するオプションがあります。

IF OBJECT_ID('new_function', 'FN') IS NULL
BEGIN
  EXEC('CREATE FUNCTION new_function() RETURNS INT AS BEGIN RETURN 1 END');
END;
go

ALTER FUNCTION new_function() RETURNS INT AS
BEGIN

...
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.