CLRを使用せずにデータベースレベルの定数(列挙)を作成しますか?


9

要求の望ましい状態に基づいて代替アクションを実行する必要があるいくつかのSQLオブジェクトがあります。ストアドプロシージャ、テーブル値関数に渡し、クエリで使用できる(CLRを使用せずに)データベースレベルの定数(列挙)を作成する方法はありますか?

CREATE PROCEDURE dbo.DoSomeWork(@param1 INTEGER, ..., @EnumValue myEnumType)  AS ...;

そしてそれを使います:

EXEC doSomeWork 85, ..., (myEnumType.EnumValue1 + myEnumType.EnumValue2);

myEnumTypeいくつかの列挙値を保持する場所。

手順では、必要な作業を実行するために、@EnumValue値を使用してテストすることができますmyEnumType。私がmyEnumType検討しているケースでは、ビットマスクの値を作成します。

簡単な例として、巨大なデータセットを受け取り、それをより小さくても非常に大きなデータセットに削減する高価なプロセスを考えてみます。このプロセスでは、そのプロセスの途中で、結果に影響を与える調整を行う必要があります。これは、削減内の中間計算のステータスに基づいて、特定のタイプのレコードをフィルターする(またはフィルターする)とします。@EnumValueタイプのはmyEnumType、このために、テストに使用することができ

SELECT   ...
FROM     ...
WHERE       (@EnumValue & myEnumType.EnumValue1 = myEnumType.EnumValue1 AND ...)
        OR  (@EnumValue & myEnumType.EnumValue2 = myEnumType.EnumValue2 AND ...)
        OR  ...

CLRを使用せずに、SQL Serverでこのようなデータベースレベルの定数を使用できますか?

ストアドプロシージャや関数などにパラメーターとして渡すことができるデータベースレベルの列挙または定数のセットを探しています

回答:


9

XMLスキーマを使用して、SQL Serverで列挙型を作成できます。

たとえば、色。

create xml schema collection ColorsEnum as '
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Color">
        <xs:simpleType>
            <xs:restriction base="xs:string"> 
                <xs:enumeration value="Red"/>
                <xs:enumeration value="Green"/>
                <xs:enumeration value="Blue"/>
                <xs:enumeration value="Yellow"/>
            </xs:restriction> 
        </xs:simpleType>
    </xs:element>
</xs:schema>';

これにより、タイプの変数またはパラメータを使用できますxml(dbo.ColorsEnum)

declare @Colors xml(dbo.ColorsEnum);
set @Colors = '<Color>Red</Color><Color>Green</Color>'

色ではないものを追加しようとすると

set @Colors = '<Color>Red</Color><Color>Ferrari</Color>';

エラーが発生します。

Msg 6926, Level 16, State 1, Line 43
XML Validation: Invalid simple type value: 'Ferrari'. Location: /*:Color[2]

このようなXMLの作成は少し面倒な場合があるため、たとえば、許可された値も保持するヘルパービューを作成できます。

create view dbo.ColorsConst as
select cast('<Color>Red</Color>' as varchar(100)) as Red,
       cast('<Color>Green</Color>' as varchar(100)) as Green,
       cast('<Color>Blue</Color>' as varchar(100)) as Blue,
       cast('<Color>Yellow</Color>' as varchar(100)) as Yellow;

このように使用して、列挙を作成します。

set @Colors = (select Red+Blue+Green from dbo.ColorsConst);

XMLスキーマから動的にビューを作成する場合は、このクエリで色を抽出できます。

select C.Name
from (select xml_schema_namespace('dbo','ColorsEnum')) as T(X)
  cross apply T.X.nodes('//*:enumeration') as E(X)
  cross apply (select E.X.value('@value', 'varchar(100)')) as C(Name);

列挙型はもちろん関数やプロシージャのパラメータとしても使用できます。

create function dbo.ColorsToString(@Colors xml(ColorsEnum))
returns varchar(100)
as
begin
  declare @T table(Color varchar(100));

  insert into @T(Color)
  select C.X.value('.', 'varchar(100)')
  from @Colors.nodes('Color') as C(X);

  return stuff((select ','+T.Color
                from @T as T
                for xml path('')), 1, 1, '');
end
create procedure dbo.GetColors
  @Colors xml(ColorsEnum)
as
select C.X.value('.', 'varchar(100)') as Color
from @Colors.nodes('Color') as C(X);
declare @Colors xml(ColorsEnum) = '
<Color>Red</Color>
<Color>Blue</Color>
';

select dbo.ColorsToString(@Colors);

set @Colors = (select Red+Blue+Green from dbo.ColorsConst);
exec dbo.GetColors @Colors;

6

どうやらSQL Server 2016を使用しているので、別の「可能な」オプションを廃止したいと思いますSESSION_CONTEXT

Leonard Lobelの記事「SQL Server 2016での状態の共有」には、SQL Server 2016のSESSION_CONTEXTこの新機能に関する非常に優れた情報があります。

いくつかの重要なポイントを要約します:

データベース接続の存続期間を通じてすべてのストアドプロシージャとバッチでセッション状態を共有したいと思ったことがあるなら、きっと気に入るはずですSESSION_CONTEXT。SQL Server 2016に接続すると、ステートフルディクショナリ、または「状態バッグ」と呼ばれるものを取得します。これには、文字列や数値などの値を格納し、割り当てたキーで値を取得できる場所があります。の場合SESSION_CONTEXT、キーは任意の文字列で、値はsql_variantです。つまり、さまざまなタイプに対応できます。

何かをに保存するとSESSION_CONTEXT、接続が閉じるまでそこに保持されます。データベースのどのテーブルにも保存されず、接続が有効である限り、メモリに保持されます。また、ストアドプロシージャ、トリガー、関数などの内部で実行されているすべてのT-SQLコードは、入力したものを共有できます SESSION_CONTEXT

これまで私たちがこれまで持ってきた最も近いものはCONTEXT_INFO、最大128バイトの長さの単一のバイナリ値を格納および共有できるようにするものSESSION_CONTEXTで、異なるデータの複数の値をサポートするで取得するディクショナリよりもはるかに柔軟性がありません。タイプ。

SESSION_CONTEXT簡単に使用できます。sp_set_session_contextを呼び出して、目的のキーごとに値を格納します。もちろん、キーと値を指定しますが、read_onlyパラメータをtrueに設定することもできます。これは、セッションコンテキストの値をロックするため、接続の残りの存続期間中は変更できません。したがって、たとえば、クライアントアプリケーションがこのストアドプロシージャを呼び出して、データベース接続を確立した直後にセッションコンテキスト値を設定するのは簡単です。これを行うときにアプリケーションがread_onlyパラメーターを設定した場合、サーバーで実行されるストアドプロシージャおよびその他のT-SQLコードは値を読み取ることしかできず、クライアントで実行されているアプリケーションによって設定されたものを変更できません。

テストとして、いくつかのCONTEXT_SESSION情報を設定するサーバーログイントリガーを作成しました-の1 SESSION_CONTEXTつがに設定されました@read_only

DROP TRIGGER IF EXISTS [InitializeSessionContext] ON ALL SERVER
GO
CREATE TRIGGER InitializeSessionContext ON ALL SERVER
FOR LOGON AS

BEGIN

    --Initialize context information that can be altered in the session
    EXEC sp_set_session_context @key = N'UsRegion'
        ,@value = N'Southeast'

    --Initialize context information that cannot be altered in the session
    EXEC sp_set_session_context @key = N'CannotChange'
        ,@value = N'CannotChangeThisValue'
        ,@read_only = 1

END;

まったく新しいユーザーとしてログインし、SESSION_CONTEXT情報を抽出することができました。

DECLARE @UsRegion varchar(20)
SET @UsRegion = CONVERT(varchar(20), SESSION_CONTEXT(N'UsRegion'))
SELECT DoThat = @UsRegion

DECLARE @CannotChange varchar(20)
SET @CannotChange = CONVERT(varchar(20), SESSION_CONTEXT(N'CannotChange'))
SELECT DoThat = @CannotChange

'read_only'コンテキスト情報を変更しようとしました:

EXEC sp_set_session_context @key = N'CannotChange'
    ,@value = N'CannotChangeThisValue'

エラーを受け取りました:

メッセージ15664、レベル16、状態1、プロシージャsp_set_session_context、行1 [バッチ開始行8]セッションコンテキストでキー 'CannotChange'を設定できません。このセッションでは、キーがread_onlyに設定されています。

ログイントリガーに関する重要な注意事項(この投稿から)!

ログオントリガーにより、sysadmin固定サーバーロールのメンバーを含むすべてのユーザーがデータベースエンジンに正常に接続できなくなる可能性があります。ログオントリガーによって接続が妨げられている場合、sysadmin固定サーバーロールのメンバーは、専用の管理者接続を使用するか、データベースエンジンを最小構成モードで起動して接続できます(-f)。


潜在的な欠点の1つは、これが(データベースごとではなく)セッションコンテキストインスタンス全体を埋めることです。この時点で、私が考えることができる唯一のオプションは次のとおりです。

  1. 名前Session_Contextと値のペアに名前を付けるには、別のデータベースで同じタイプ名の衝突を引き起こさないように、データベース名を前に付けます。これはSession_Context、すべてのユーザーのすべての名前値を事前定義する問題を解決しません。
  2. ログイントリガーが起動すると、EventData(xml)にアクセスしてログインユーザーを抽出し、それに基づいて特定のSession_Context名前と値のペアを作成できます。

4

SQL Serverではできません(ただし、1998年にOracleパッケージで定数を作成したことを思い出して、SQL Serverで定数を取得できなかったのですが)。

そして、私はテストしたところ、SQLCLRでこれを行うことさえできないことがわかりました。少なくとも、すべてのケースで機能するという意味ではそうではありません。ホールドアップは、ストアドプロシージャパラメータの制限です。パラメータ名に.またはを含めることはできないようです::。私は試した:

EXEC MyStoredProc @ParamName = SchemaName.UdtName::[StaticField];

-- and:

DECLARE @Var = SchemaName.UdtName = 'something';
EXEC MyStoredProc @ParamName = @Var.[InstanceProperty];

どちらの場合も、次のSET PARSEONLY ON;理由により、解析フェーズ(を使用して検証)を通過できませんでした。

メッセージ102、レベル15、状態1、行xxxxx
'。'の近くに不正な構文があります。

一方、どちらの方法もユーザー定義関数のパラメーターに対しては機能しました。

SELECT MyUDF(SchemaName.UdtName::[StaticField]);

-- and:

DECLARE @Var = SchemaName.UdtName = N'something';
SELECT MyUDF(@Var.[InstanceProperty]);

したがって、SQLCLRを使用してUDF、TVF、UDA(私は想定)、およびクエリで直接機能するものを作成し、ストアドプロシージャで使用する必要がある場合はローカル変数に割り当てることが、実際にできる最善の方法です。

DECLARE @VarInt = SchemaName.UdtName::[StaticField];
EXEC MyStoredProc @ParamName = @VarInt;

これは、実際の列挙値を取得する機会があるときに使用したアプローチです(その使用法/意味に固有のルックアップテーブルにあるはずのルックアップ値とは対照的です)。


ユーザー定義関数(UDF)でこれを試行して「定数」/「列挙型」の値を吐き出すことに関しては、ストアドプロシージャのパラメーターとして渡すという点でも機能しませんでした。

EXEC MyStoredProc @ParamName = FunctionName(N'something');

文字列を数値に置き換えた場合でも、SSMSが括弧内のすべてを強調表示する「不正な構文」エラー、または渡すパラメーターがない場合は右括弧を返します。

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