web-dev-qa-db-ja.com

テーブル変数関数の一意制約に名前を付ける方法は?

データベースオブジェクトの命名規則に合わせて、いくつかの一意の制約の名前を変更しています。奇妙なことに、次のように一意の制約を持つテーブルを返す複数行のテーブル値関数があります。

CREATE FUNCTION [dbo].[fn_name] (...)
RETURNS @Result 
TABLE
(
    ID BIGINT PRIMARY KEY,
    ...
    RowNum BIGINT UNIQUE 
)
BEGIN
    ...
    RETURN
END
GO

このように名前を付けようとしましたが、機能しません。

CREATE FUNCTION [dbo].[fn_name] (...)
RETURNS @Result 
TABLE
(
    ID BIGINT PRIMARY KEY,
    ...
    RowNum BIGINT
    ,CONSTRAINT UC_fn_name_RowNum UNIQUE([RowNum])  
)
BEGIN
    ...
    RETURN
END
GO

テーブル変数関数定義の一部である場合、一意性制約の名前を設定することは可能ですか?

7
gotqn

テーブル変数は、DECLAREから作成されたか、CREATE FUNCTIONステートメントのRETURNSから作成されたかにかかわらず、ユーザー定義のテーブルタイプ(UDTT)と同様に、名前付き。 CREATE FUNCTION のMSDNページによると:

引数
...
<column_constraint> :: =および<table_constraint> :: =
...名前付きの制約は許可されません。

このルールの「明記された」例外は、MSDNページの DECLARE @local_variable で示されます(の説明の下に)[〜#〜]デフォルト[〜#〜] ):

以前のバージョンのSQL Serverとの互換性を維持するために、制約名をDEFAULTに割り当てることができます。

ただし、DEFAULT制約に名前を割り当てることができないため、これはドキュメントのエラーのようです。これを行うための構文の説明はなく、ドキュメントにこれを行う例はありません(私はSQL Server 2000まで)。

一時テーブルに関しては、問題はもう少し単純です。制約に明示的に名前を付けると、コードが2つの異なるセッションで同時に実行できなくなります。制約は(インデックスとは異なり)オブジェクトであり、同じスキーマ内で一意の名前を持つ必要があります。したがって、一部のコードが、名前が指定された制約を持つローカル一時テーブルを作成する場合、2つのセッションがそのコードを同時に実行すると、2番目のセッションが名前の競合により一時テーブルを作成しようとしたときにエラーが発生します。 (これは、一時テーブルが存在するtempdbで発生します)。たとえば、SSMSの1つのクエリタブで次を実行します。

CREATE TABLE #Session1 (Col1 INT NOT NULL CONSTRAINT [UQ_DifferentSession] UNIQUE);

そして、別のクエリタブで、以下を実行します:

CREATE TABLE #Session2 (Col1 INT NOT NULL CONSTRAINT [UQ_DifferentSession] UNIQUE);

異なるセッションで作成されたローカルの一時テーブルであることに加えて、異なる名前の一時テーブルがある場合でも、次のエラーを受け取ります。

メッセージ2714、レベル16、状態5、行1
データベースにはすでに「UQ_DifferentSession」という名前のオブジェクトがあります。
メッセージ1750、レベル16、状態0、行1
制約を作成できませんでした。以前のエラーを参照してください。

一時テーブルと同様に、テーブル変数も "宣言"されるまで定義を持たず、その定義はtempdbにのみ存在します。ただし、制約に名前を付けることができないため、テーブル変数の名前の衝突をテストすることはできません。

一方、マルチステートメントTVFで返されるテーブルとユーザー定義のテーブルタイプは、それらが存在するデータベースに定義を格納するという点で、一時テーブルとは少し異なります。ただし、ローカルデータベースに格納されているメタデータが実際にデータの格納に使用されることはありません。それらのメタデータは、tempdbで(必要に応じて)作成されるもののテンプレートとしてのみ使用されます。そして、そのメタデータ「テンプレート」が使用されると、tempdbの名前付きオブジェクト(主キー、一意の制約、デフォルトの制約、およびチェックの制約)は、UQ__#BC4FB52__A259EE56FAB8BB6Fの形式で動的に生成された名前を取得します。


命名規則に沿うために制約に名前を付けたいという要望について(質問へのコメントで述べられているように):

(@Peterの answer で指摘されているように)sp_renameを使用しても何も害はありませんが、表面的な変更にすぎません。命名規則を持つ主な理由の1つはメンテナンスのためです。そのため、後でオブジェクトをALTERまたはDROPする必要がある場合、そこにあるものを見つけるためにクエリを記述する必要はありません。そして、それを動的SQLに連結します。しかし、いずれにしてもそれらをALTERまたはDROPする方法がないため、その懸念はここでは関係ありません。

また、sp_renameを介して行われた名前の変更は、ALTER FUNCTIONが制約を削除して再作成し、新しく生成されたオブジェクト名を与えるため、短命になります。これは、CREATE FUNCTIONALTER FUNCTION、およびCREATE TYPEステートメントをキャプチャするDDLトリガーを使用してプログラムで処理できる可能性がありますが、その努力の最終結果は、あなたの人生が簡単になるということではありません;-)。 sys.objectssys.indexes、およびsys.key_constraintsから選択すると、「よりクリーン」に見えるだけです。

8
Solomon Rutzky