に相当するものはありますか
PRINT 'hello world'
これはCLR(C#)コードから呼び出すことができますか?
関数にデバッグ情報を出力しようとしています。これはリモートサーバーであるため、VSデバッガーを実行できません。
ありがとう!
に相当するものはありますか
PRINT 'hello world'
これはCLR(C#)コードから呼び出すことができますか?
関数にデバッグ情報を出力しようとしています。これはリモートサーバーであるため、VSデバッガーを実行できません。
ありがとう!
答えは、あなたが同等のことをすることはできないということです
PRINT 'Hello World'
内側から[SqlFunction()]
。ただし、[SqlProcedure()]
を使用してそれを行うことができます
SqlContext.Pipe.Send("hello world")
これは、関数内にPRINTを貼り付けると、「関数内での副作用演算子「PRINT」の使用が無効です」というエラーが発生するT-SQLと一致しています。ただし、ストアドプロシージャから実行する場合はそうではありません。
回避策については、次のことをお勧めします。
List<string> messages
の内容を返す別のテーブル値関数を記述しますmessages
。もちろん、messages
複数のスレッドが同時にアクセスしようとする可能性があるため、へのアクセスを同期する必要があります。[SqlProcedure()]
あなたはただできるはずです:
SqlContext.Pipe.Send("hello world");
これをCLRUDF内で実行している場合は、SqlContext.Pipe
常にnull
検出したとおりになります。有効なSqlPipe
ものがなければ、私はあなたがあなたが望むことをすることができるとは思わない。
これが純粋にデバッグ目的である場合は、いつでもマネージコード内のファイルを開いて、そこに出力を書き込むことができます。ただし、これにはアセンブリにEXTERNAL_ACCESS
権限が必要です。また、データベースに信頼できるものとしてマークを付ける必要があります。必ずしも私がすることやお勧めすることではありません。
SQLCLR 関数 -- スカラー ユーザー定義関数 (UDF)、テーブル値関数 (TVF)、ユーザー定義集計 (UDA)、およびユーザー定義型 (UDT) 内のメソッド -- コンテキスト接続 (つまり、ConnectionString = )、または"Context Connection = true;"
ができないなど、T-SQL 関数がバインドされているのと同じ制限のほとんどによってバインドされます。ただし、いくつかのオプションがあります。PRINT
RAISERROR('message', 10, 1)
これらのオプションに入る前に、次のことを述べておく必要があります。
ストアド プロシージャの使用に切り替える必要はありません。関数が必要な場合は、関数に固執します。
UDF (T-SQL および SQLCLR) 関数ではオーバーロードが許可されていないため、「デバッグ」パラメーターを追加して出力を変更するのは少し極端に思えます。したがって、デバッグ パラメータは常にシグネチャに含まれます。デバッグをトリガーする場合は、#debug
(またはそのようなもの) という名前の一時テーブルを作成し、ConnectionString をSELECT OBJECT_ID(N'tempdb..#debug');
使用"Context Connection = true;"
してテストします (これは高速で、SAFE モードで実行でき、同じセッションの一部であるため、一時テーブル)。からその結果を取得しif (SqlCommand.ExecuteScalar() == DBNull.Value)
ます。
グローバル (静的) 変数は使用しないでください。これは必要以上に複雑であり、(通常) Assembly を に設定するUNSAFE
必要がありますが、これは可能な限り回避する必要があります。
したがって、少なくともアセンブリを に設定できるEXTERNAL_ACCESS
場合は、いくつかのオプションがあります。また、これを行うためにデータベースを に設定する必要はありませんTRUSTWORTHY ON
。これは非常に一般的な (そして残念な) 誤解です。アセンブリに署名する必要があるだけであり (これは良い方法です)、次に[master]
DLL から非対称キーを作成し (で)、その非対称キーに基づいてログインを作成し、最後に Login を付与しEXTERNAL ACCESS ASSEMBLY
ます。それを(1回)行った後、次のいずれかを実行できます。
File.AppendAllText (String path, String contents)を使用してメッセージをファイルに書き込みます。もちろん、ファイル システムにアクセスできない場合、これはあまり役に立ちません。ネットワーク上にアクセス可能な共有ドライブがある場合、SQL Server サービスのサービス アカウントがその共有でファイルを作成および書き込みする権限を持っている限り、これは機能します。サービス アカウントには権限がないが、ドメイン / Active Directory アカウントには権限がある共有がある場合は、そのFile.AppendAllText
呼び出しを次のようにラップできます。
using (WindowsImpersonationContext _Impersonate =
SqlContext.WindowsIdentity.Impersonate())
{
File.AppendAllText("path.txt", _DebugMessage);
_Impersonate.Undo();
}
SQL Server に接続し、メッセージをテーブルに書き込みます。現在/ローカルの SQL Server またはその他の SQL Server を指定できます。[tempdb]
次回 SQL Server を再起動したときに自動的にクリーンアップされるようにテーブルを作成できますが、それ以外の場合はそのときまで、または削除するまで存続します。通常/外部接続を作成すると、DML ステートメントを実行できます。次に、関数の実行中にテーブルから選択できます。
メッセージを環境変数に書き込みます。環境変数は、実際には改行を処理しませんが、Vista / Server 2008 以降、サイズが正確に制限されていません。ただし、.NET コード内で設定された変数は、SQL Server サービスが再起動されるまで存続します。また、現在の値を読み取り、新しいメッセージを最後に連結することで、メッセージを追加できます。何かのようなもの:
{
string _Current = System.Environment.GetEnvironmentVariable(_VariableName,
EnvironmentVariableTarget.Process);
System.Environment.SetEnvironmentVariable(
_VariableName,
_Current + _DebugMessage,
EnvironmentVariableTarget.Process);
}
これら 3 つのケースのそれぞれで、テストがシングルスレッド方式で行われていると想定されていることに注意してください。関数が複数のセッションから同時に実行される場合は、メッセージを分離する方法が必要です。その場合、現在の「transaction_id」(トランザクションでなくてもすべてのクエリBEGIN TRAN
!) を取得できます。これは、特定の実行に対して一貫している必要があります (同じ関数で複数回使用されている場合や、関数がそれぞれごとに呼び出されている場合)。複数の行にまたがる行)。この値は、ファイルまたは環境変数メソッドを使用する場合はメッセージのプレフィックスとして使用でき、テーブルに格納する場合は個別のフィールドとして使用できます。次の手順でトランザクションを取得できます。
int _TransactionID;
using (SqlConnection _Connection = new SqlConnection("Context Connection = true;"))
{
using (SqlCommand _Command = _Connection.CreateCommand())
{
_Command.CommandText = @"
SELECT transaction_id
FROM sys.dm_exec_requests
WHERE session_id = @@SPID;
";
_Connection.Open();
_TransactionID = (int)_Command.ExecuteScalar();
}
}
T-SQL および SQLCLR 関数に関する追加情報
次のリストは、最初にユーザー定義関数の作成 (データベース エンジン)の MSDN ページから取得したものであり、前述のように、T-SQL 関数と SQLCLR 関数の違いを反映するために私が編集したものです。
- ユーザー定義関数を使用して、データベースの状態を変更するアクションを実行することはできません。
- ユーザー定義関数には、ターゲットとしてテーブルを持つ OUTPUT INTO 句を含めることはできません。
- ユーザー定義関数は、複数の結果セットを返すことはできません。複数の結果セットを返す必要がある場合は、ストアド プロシージャを使用します。
- ユーザー定義関数では、エラー処理が制限されています。UDF は、TRY…CATCH、@@ERROR、または RAISERROR をサポートしていません。[注: これは、ネイティブまたは SQLCLR 関数から送信された T-SQL に関するものです。.NET コードで try / catch / finally / throw を使用できます。]
- ユーザー定義関数では SET ステートメントを使用できません。
- FOR XML 句は使用できません
- ユーザー定義関数はネストできます。... ネスト レベルは、呼び出された関数が実行を開始するとインクリメントされ、呼び出された関数が実行を終了するとデクリメントされます。ユーザー定義関数は、最大 32 レベルまでネストできます。
- 次の Service Broker ステートメントは、Transact-SQL ユーザー定義関数の定義に含めることはできません。
- ダイアログの会話を開始
- 会話を終了
- 会話グループを取得
- 会話を移動
- 受け取る
- 送信
以下は、T-SQL 関数と SQLCLR 関数の両方に関係します。
- 印刷できません
- NEWID() を呼び出すことはできません [まあ、
SELECT NEWID()
ビュー内からでない限り。ただし、.NET コード内では、Guid.NewGuid()
. ]
以下は、T-SQL 関数にのみ関係します。
- ユーザー定義関数は、ストアド プロシージャを呼び出すことはできませんが、拡張ストアド プロシージャを呼び出すことはできます。
- ユーザー定義関数は、動的 SQL または一時テーブルを使用できません。テーブル変数を使用できます。
対照的に、SQLCLR 関数は次のことができます。
- 読み取り専用である限り、ストアド プロシージャを実行します。
- 動的 SQL を利用します (SQLCLR から送信されたすべての SQL は、その性質上アドホック/動的です)。
- 一時テーブルから選択します。
Ahh I see... Jsut to clarify: if you have a SqlFunction then SqlContext.Pipe is not available, however in an SqlProcedure it is and you can use Send() to write messages.
I still haven't found a way to output information from a SqlFunction aside from an exception message.
これらの情報は、「xp_logevent」ストアド プロシージャを介して配置することができます。デバッグ情報は、さまざまなレベルで「情報」、「警告」、または「エラー」として設定できます。また、これらのデバッグ/エラー情報をイベントログに記録しようとしましたが、セキュリティで少し構成が必要であり、本番環境で使用できないとは思えません。