4

パラメータ置換後に完全な SQL ステートメントを取得する簡単な方法がある場合は? つまり、このプログラムが実行するすべての SQL のログファイルを保持したいと考えています。

または、これを行いたい場合は、パラメーターを取り除き、クエリ全体を古い学校の方法で 1 つの大きな文字列で実行したいだけですか?

簡単な例: 出力をキャプチャしたい:

SELECT subcatId FROM EnrollmentSubCategory WHERE catid = 1

.. このコードから:

    Dim subCatSQL As String = "SELECT subcatId FROM EnrollmentSubCategory WHERE catid = @catId"
    Dim connectionString As String = "X"
    Dim conn As New SqlConnection(connectionString)
    If conn.State = ConnectionState.Closed Then
        conn.Open()
    End If
    Dim cmd As New SqlCommand(subCatSQL, conn)
    With cmd
        .Parameters.Add(New SqlParameter("@catId", SqlDbType.Int, 1)) 
    End With

    Console.WriteLine("Before: " + cmd.CommandText)
    cmd.Prepare()
    Console.WriteLine("After: " + cmd.CommandText)

私は Prepare() が置換を行うと思っていましたが、明らかにそうではありません。

考え?アドバイス?前もって感謝します。

4

7 に答える 7

6

いいえ、.Prepareそれをしません、そして実際、コマンドの何もしません。パラメータ値が実際のコマンド文字列に置き換えられることはありません。これは個別にDBに送信されますが、これはいくつかの理由で有効です。

  • これにより、sqlserver(または他のデータベース)がクエリのクエリプランをキャッシュし、次回再利用できるようになります。毎回異なるコマンドテキスト文字列がデータベースに送信されると、データベースは毎回計画を作成する必要があります。
  • パラメータをコンパートメント化されたdbに送信することにより、SQLインジェクション攻撃に対する自然防御があります。

本当に古いデータベース(SQL Server 7?)を使用している場合を除いて、.Prepare()必要ではなく、実際には何もしません。以前はサーバー上でクエリをコンパイルするという点で便利でしたが、現在は自動的に実行されます。久しぶり.Prepare()です。

うーん、うーん。ここを見ると.Prepare()、まだ何かが行われているようです。パラメータで定義された長さよりも大きい値がある場合、.Prepare()は値を切り捨てるので、実行時にエラーが発生せず、クエリは成功します。いいね。

于 2010-05-19T17:48:00.780 に答える
5

私は通常、DbCommand オブジェクト (OracleCommand、SqlCommand などの親クラス) を指定して、クエリとパラメーターの値をログに記録するユーティリティ関数をプロジェクトに追加します。次のようになります。

Public Shared Sub LogQuery(ByRef cmd As DbCommand)
    If cmd.CommandText Is Nothing Or cmd.CommandText.Length = 0 Then
        Return
    End If

    Dim logFile As CLog = New CLog("sql.log")
    Dim msg As New StringBuilder

    msg.AppendLine("*** Query ***")
    msg.AppendLine(cmd.CommandText)
    msg.AppendLine("*** End Query ***")
    msg.AppendLine("*** Parameters ***")

    For Each p As DbParameter In cmd.Parameters
        msg.AppendLine(String.Format("{0}: {1}", p.ParameterName, p.Value.ToString()))
    Next

    msg.AppendLine("*** End Parameters ***")

    logFile.WriteLog(msg.ToString() & System.Environment.NewLine)
End Sub

これを書いているときに、パラメーター値を個別にログに記録する代わりに、少し String.Replace()ing を実行して、パラメーター値をクエリに置き換えてログに記録できることに気付きました。

For Each p As DbParameter In cmd.Parameters
    msg = msg.Replace(p.ParameterName, p.Value.ToString())
Next
于 2010-05-19T19:12:17.410 に答える
2

パラメータは置き換えられません。sqlはそのままで、パラメーター自体がパラメーターとしてsp_executesqlシステムストアドプロシージャに渡されます。

于 2010-05-19T17:48:11.777 に答える
1

SQL Serverに送信されるのは、値で置き換えられた変数ではなく、プリペアドステートメントであるため、この方法で再利用できます。

SQL Serverでプロファイラーを開くと、実行されているものがSELECT subcatId FROM EnrollmentSubCategory WHERE catid=1ではないことがわかります。

于 2010-05-19T17:48:09.623 に答える
0

これは最近ここで尋ねられました。他の回答が指摘しているように、パラメーターは帯域外で送信されます。

プロファイラーは、サーバーでキャプチャする場合に使用したいものであり、トレースを作成して起動ジョブとして使用できます。

于 2010-05-19T18:00:30.983 に答える
0

プロバイダーの周りにラッパーを構築できSystem.Data.SqlClientます (*例: 構成ファイルに登録されているプロバイダー... * providerName="System.Data.SqlClient")。基本的にインターセプト プロキシは、プロバイダーを通過するすべての情報にアクセスでき、必要なものを吸い上げ、集約および/または強化して、ログに送信できます。これはもう少し高度ですが、全範囲の情報を取得するための扉を開き、別の懸念事項として挿入/置換/削除できます。

于 2010-05-19T18:05:53.360 に答える