61

パラメータをSQLCommandに渡す最良の方法は何ですか? できるよ:

cmd.Parameters.Add("@Name", SqlDbType.VarChar, 20).Value = "Bob";

また

cmd.Parameters.Add("@Name", SqlDbType.VarChar).Value = "Bob";

また

cmd.Parameters.Add("@Name").Value = "Bob";

最初のものは、パフォーマンスに関してもエラーチェックに関しても、何らかの形で「優れている」ようです。でも、もっとはっきりと知りたいです。

4

5 に答える 5

67

何が起こっているのですか?

のいくつかのオーバーロードのパラメーター リストを引用しますAddSqlParameterこれらは、クラスのコンストラクター オーバーロードに直接対応する便利なメソッドです。基本的に、呼び出した便利なメソッドと同じシグネチャを持つコンストラクターを使用してパラメーター オブジェクトを構築し、次のSqlParameterCollection.Add(SqlParameter)ように呼び出します。

SqlParameter foo = new SqlParameter(parameterName, dbType, size);
this.Add(foo);

AddWithValueは似ていますが、さらに利便性を高め、値も設定します。ただし、実際にはフレームワークの欠陥を解決するために導入されました。MSDNを引用するには、

Add文字列とオブジェクトを受け取る のオーバーロードは非推奨になりました。文字列で整数を渡すと、パラメーター値または対応する値のいずれかとして解釈される可能性がある列挙値と文字列を受け取るオーバーロードとあいまいになる可能性 が ある ためSqlParameterCollection.Addです。 名前と値を指定してパラメーターを追加する場合はいつでも使用します。StringSqlDbTypeSqlDbTypeAddWithValue

クラスのコンストラクターのオーバーロードは、SqlParameterインスタンス プロパティを設定するための単なる便利なものです。これらはコードを短縮しますが、パフォーマンスへの影響はわずかです。コンストラクターはセッター メソッドをバイパスし、プライベート メンバーを直接操作する可能性があります。差があれば大したことない。

私は何をすべきか?

以下に注意してください(MSDNから)

双方向および出力パラメータ、および戻り値については、 の値を設定する必要がありますSize。これは入力パラメーターには必要ありません。明示的に設定されていない場合、パラメーター化されたステートメントが実行されるときに、指定されたパラメーターの実際のサイズから値が推測されます。

デフォルトのタイプは入力です。ただし、サイズをこのように推測できるようにし、パラメーター オブジェクトをループでリサイクルする場合 (パフォーマンスに関心があると言いました)、サイズは最初の値によって設定され、それよりも長い後続の値は次のようになります。切り取った。明らかに、これは文字列などの可変長の値に対してのみ重要です。

ループ内で同じ論理パラメーターを繰り返し渡す場合は、ループの外で SqlParameter オブジェクトを作成し、適切なサイズにすることをお勧めします。varchar のサイズを大きくしても問題はありません。そのため、正確な最大値を取得するのが PITA である場合は、列が予想されるよりも大きく設定してください。反復ごとに新しいオブジェクトを作成するのではなく、オブジェクトをリサイクルしているため、オーバーサイジングに少し興奮したとしても、ループ中のメモリ消費はおそらく減少します。

正直なところ、何千もの呼び出しを処理しない限り、どれも大きな違いはありません. AddWithValueサイジングの問題を回避して、新しいオブジェクトを作成します。短くて甘くてわかりやすいです。数千をループする場合は、私のアプローチを使用してください。そうでない場合は、 を使用AddWithValueして、コードをシンプルで維持しやすくします。


2008年は遠い昔

私がこれを書いてから数年で、世界は変わりました。新しい種類の日付があり、日付に関する最近の問題が拡大の意味について考えるまで、私の頭をよぎらなかった問題もあります。

拡大と縮小は、用語に慣れていない人にとっては、データ型変換の性質です。double に int を代入しても、double の方が「幅が広い」ため、精度が失われることはありません。これを行うことは常に安全であるため、変換は自動的に行われます。これが、int を double に割り当てることができる理由ですが、別の方法で明示的なキャストを行う必要があります。double から int への変換は、精度が失われる可能性のある縮小変換です。

これは文字列にも当てはまります。NVARCHAR は VARCHAR よりも幅が広いため、VARCHAR を NVARCHAR に割り当てることができますが、それ以外の方法ではキャストが必要です。VARCHAR は暗黙的に NVARCHAR に拡大されるため、比較は機能しますが、これはインデックスの使用を妨げます!

C# 文字列は Unicode であるため、AddWithValue は NVARCHAR パラメーターを生成します。もう一方の端では、比較のために VARCHAR 列の値が NVARCHAR に拡張されます。これはクエリの実行を停止しませんが、インデックスが使用されないようにします。これは悪いです。

それについて何ができますか?考えられる解決策は 2 つあります。

  • パラメータを明示的に入力します。これは、AddWithValue がなくなることを意味します
  • すべての文字列列の型を NVARCHAR に変更します。

VARCHAR を捨てるのがおそらく最良のアイデアです。これは、予測可能な結果を​​伴う単純な変更であり、ローカリゼーション ストーリーを改善します。ただし、これをオプションとして使用できない場合があります。

最近では、直接 ADO.NET をあまり使用していません。Linq2Sql は現在、私が選んだ武器であり、この更新プログラムを作成するという行為により、この問題をどのように処理するのか疑問に思いました。VARCHAR 列を介したルックアップのために自分のデータ アクセス コードをチェックしたいという欲求が突然燃え上がりました。


2019年、世界は再び動き出した

Linq2Sql は dotnet Core では使用できないため、Dapper を使用しています。[N]VARCHAR の問題はまだ解決されていませんが、埋もれているわけではありません。ADO を使用することもできると思いますので、その点で一巡しました。

于 2008-11-17T06:00:01.017 に答える
59

も使用できますがAddWithValue()、暗黙的な型変換が間違っている可能性があることに注意してください。

cmd.Parameters.AddWithValue("@Name", "Bob");
于 2008-11-16T00:59:41.170 に答える
7

私はあなたのオプション1を使用していました:

cmd.Parameters.Add("@Name", SqlDbType.VarChar, 20).Value = "ボブ";

これはうまくいきましたが、その後 .AddWithValue を使い始めました。何千回も使用しても問題はありませんでした。ほとんどの場合、クラスにプライベート変数を渡すので、暗黙の型変換についてそれほど心配する必要はありません。

于 2008-11-16T03:30:00.537 に答える
5

間違いなくナンバー1だと思います。ただし、Microsoft はエンタープライズ ライブラリのデータ アクセス アプリケーション ブロックでそれを行っていますが、特に SQL サーバーには最適です。

http://msdn.microsoft.com/en-us/library/dd203144.aspx

于 2008-11-16T01:17:57.043 に答える
3

アプリケーションによって異なります。ストアドプロシージャパラメータの長さを変更してもDAOを変更する必要がないため、実際には2が好きです。それは私だけです。パフォーマンスのペナルティなどがあるかどうかはわかりません。

于 2008-11-17T04:41:28.443 に答える