web-dev-qa-db-ja.com

ストアドプロシージャのSqlParameterでのDateTimeの使用、フォーマットエラー

DateTimeの値としてSqlParameterを使用して、C#、.NET 2.0から(SQL 2005サーバー上の)ストアドプロシージャを呼び出そうとしています。ストアドプロシージャのSQL型は 'datetime'です。

SQL Management Studioからsprocを実行すると正常に機能します。しかし、C#から呼び出すたびに、日付形式に関するエラーが発生します。

SQL Profilerを実行して呼び出しを監視するとき、exec呼び出しをコピーして貼り付け、何が起こっているのかを確認します。これらは、私が試みたことに関する私の観察とメモです。

1)DateTimeDateTimeとして直接渡すか、SqlDateTimeに変換すると、フィールドは次のように単一引用符のペアで囲まれます。

_@Date_Of_Birth=N''1/8/2009 8:06:17 PM''
_

2)DateTimeを文字列として渡すと、一重引用符のみが取得されます

3)SqlDateTime.ToSqlString()を使用しても、UTC形式の日時文字列は生成されません(世界時に変換した後でも)

4)DateTime.ToString()を使用しても、UTC形式の日時文字列は生成されません。

5)DbTypeSqlParameterDateTimeに手動で設定しても、上記の観察結果は変わりません。

それで、私の質問は、どのようにして適切にフォーマットされた時間をSqlParameterに渡すためにC#を取得するのですか?確かにこれは一般的なユースケースですが、なぜ作業するのがそれほど難しいのでしょうか? DateTimeをSQL互換の文字列に変換できないようです(例: '2009-01-08T08:22:45')

[〜#〜] edit [〜#〜]

RE:BFree、sprocを実際に実行するコードは次のとおりです。

_using (SqlCommand sprocCommand = new SqlCommand(sprocName))
{
    sprocCommand.Connection = transaction.Connection;
    sprocCommand.Transaction = transaction;
    sprocCommand.CommandType = System.Data.CommandType.StoredProcedure;
    sprocCommand.Parameters.AddRange(parameters.ToArray());
    sprocCommand.ExecuteNonQuery();
}
_

私が試したことについてさらに詳しく説明します:

_parameters.Add(new SqlParameter("@Date_Of_Birth", DOB));

parameters.Add(new SqlParameter("@Date_Of_Birth", DOB.ToUniversalTime()));

parameters.Add(new SqlParameter("@Date_Of_Birth", 
    DOB.ToUniversalTime().ToString()));

SqlParameter param = new SqlParameter("@Date_Of_Birth", 
    System.Data.SqlDbType.DateTime);
param.Value = DOB.ToUniversalTime();
parameters.Add(param);

SqlParameter param = new SqlParameter("@Date_Of_Birth", 
    SqlDbType.DateTime);
param.Value = new SqlDateTime(DOB.ToUniversalTime());
parameters.Add(param);

parameters.Add(new SqlParameter("@Date_Of_Birth", 
    new SqlDateTime(DOB.ToUniversalTime()).ToSqlString()));
_

追加の編集

私が最もうまくいくと思ったもの:

_SqlParameter param = new SqlParameter("@Date_Of_Birth",  
    System.Data.SqlDbType.DateTime);
param.Value = DOB;
_

SQLプロファイラーで見られるように、exec呼び出しでこの値が生成されます。

_@Date_Of_Birth=''2009-01-08 15:08:21:813''
_

これを次のように変更した場合:

_@Date_Of_Birth='2009-01-08T15:08:21'
_

動作しますが、単一引用符のペアでは解析されず、日付と時刻の間のスペースと最後のミリ秒でDateTimeに正しく変換されません。

更新と成功

下からのリクエストの後、上のコードをコピー/貼り付けました。簡潔にするために、あちこちで物事をトリミングしました。私の問題は、私が除外したコードにあったことがわかりました。あなたが誰でもすぐに見つけたはずです。トランザクション内にsproc呼び出しをラップしました。私は単にtransaction.Commit() !!!!!をしていないことがわかりました私はそれを言うのを恥ずかしく思いますが、あなたはそれを持っています。

プロファイラーから返される構文で何が起こっているのかまだわかりません。同僚が自分のコンピューターからプロファイラーのインスタンスを監視し、適切な構文を返しました。私のプロファイラーからのまさに同じ実行を見ると、誤った構文が示されました。それは赤字として機能し、はるかに単純で真の答えではなく、クエリ構文の問題があると信じさせました。それは、トランザクションをコミットする必要があるということでした!

私は以下の回答を正しいとマークし、他の人が私の特定の(脳の失効)問題を修正しなくても質問に答えたので、他の人にいくつかの賛成票を投げました。

22
Matt

SqlParameter をどのように設定していますか? SqlDbTypeプロパティSqlDbType.DateTime その後、パラメータに直接 DateTime を渡します(文字列に変換しないでください。その場合、一連の問題が発生します)。

DBに値を取得できるはずです。そうでない場合、これを行う方法の非常に簡単な例を次に示します。

static void Main(string[] args)
{
    // Create the connection.
    using (SqlConnection connection = new SqlConnection(@"Data Source=..."))
    {
        // Open the connection.
        connection.Open();

        // Create the command.
        using (SqlCommand command = new SqlCommand("xsp_Test", connection))
        {
            // Set the command type.
            command.CommandType = System.Data.CommandType.StoredProcedure;

            // Add the parameter.
            SqlParameter parameter = command.Parameters.Add("@dt",
                System.Data.SqlDbType.DateTime);

            // Set the value.
            parameter.Value = DateTime.Now;

            // Make the call.
            command.ExecuteNonQuery();
        }
    }
}

ここでの問題の一部は、時刻がUTCであるという事実がSQL Serverに伝達されていないことを心配していることだと思います。 SQL Serverは特定の時刻が特定のロケール/タイムゾーンにあることを知らないため、そのためには、すべきではありません。

UTC値を保存する場合は、SQL Serverに渡す前にUTCに変換します(サーバーがDateTimeを生成するクライアントコードと同じタイムゾーンを持っている場合を除き、それでもリスクがあります。 IMO)。 SQL Serverはこの値を保存し、取得したときにローカル時間で表示したい場合は、自分で実行する必要があります(DateTime構造体は簡単に実行できます)。

つまり、変換を実行し、変換されたUTC日付(文字列への変換ではなく ToUniversalTime method を呼び出して取得した日付)を渡すと、ストアドプロシージャ。

値を取得したら、 ToLocalTime method を呼び出して、ローカルタイムゾーンの時刻を取得します。

29
casperOne

パラメータを追加する方法は次のとおりです。

sprocCommand.Parameters.Add(New SqlParameter("@Date_Of_Birth",Data.SqlDbType.DateTime))
sprocCommand.Parameters("@Date_Of_Birth").Value = DOB

DOBを書き出すときに引用符がないと仮定しています。

サードパーティのコントロールを使用して日付を取得していますか?それらのいくつかからテキスト値が生成される方法に問題がありました。

最後に、DOBを参照せずにパラメーターの.Value属性を入力すると機能しますか?

3
K Richard

Microsoft.ApplicationBlocks.Dataを使用すると、sprocの呼び出しが1行になります。

SqlHelper.ExecuteNonQuery(ConnectionString, "SprocName", DOB)

ああ、私はcasperOneが正しいと思います...複数のタイムゾーンで正しい日時を確保したい場合は、値をSQL Serverに送信する前にUTCに変換するだけです

SqlHelper.ExecuteNonQuery(ConnectionString, "SprocName", DOB.ToUniversalTime())
0

ただ使用する:

 param.AddWithValue("@Date_Of_Birth",DOB);

それはすべての問題を処理します。

0
BFree