web-dev-qa-db-ja.com

varcharデータ型をdatetimeデータ型に変換すると、範囲外の値が発生しました

11月に作成されたすべての行を取得する簡単なクエリを実行しようとしています。

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN '2014-11-01 00:00:00.000' 
AND '2014-11-30 23:59:59.997';

SMSSは次を返します:

Varcharデータ型をdatetimeデータ型に変換すると、範囲外の値が発生しました。

'Created'がdatetimeに設定されているときに、データがvarcharからdatetimeに変換される理由がわかりません。

Columns 「作成済み」が日時であることをサーバーに通知する必要がありますか?そうでない場合、なぜこのvarcharメッセージが表示されるのですか?

編集:データベースの値はYYYY-MM-DD。以下の@SqlZimからの返信では、convert()を使用してSQLに日付がdb内のどの形式であるかを伝え、スペース文字を文字Tで置き換える必要があると述べています。

select count(*) 
from dbo.profile 
where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
and convert(datetime,'2014-11-30T23:59:59.997');`
8
jedluddley

私はあなたのプロフィールをチェックしたところ、あなたがイギリスにいることがわかりました。 SQLサーバーがdateformat dmyを使用するように設定されている場合は、それが問題を説明しています。日時文字列のスペースの代わりに「T」を使用しないと、SQL ServerはそれをISO8601形式として認識しません。

これを試して:

select count(*) 
  from dbo.profile 
  where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
                      and convert(datetime,'2014-11-30T23:59:59.997');

日付や日付時刻を使用してクエリを実行するのは難しい場合があります。

編集:エラーメッセージの範囲外の値を明確にするには、月を30と解釈し、日を11と解釈することになります。

8
SqlZim

'Created'がdatetimeに設定されているときにデータがvarcharからdatetimeに変換される理由がわかりません

Created列との比較のために提供しているリテラルは文字列です。これらのリテラルをdatetime列と比較するために、SQL Serverは データ型の優先順位 の規則に従って、文字列をdatetime型に変換しようとします。文字列の形式に関する明示的な情報がない場合、SQL Serverは文字列を日時として解釈するために 複雑な規則 に従います。

私の見解では、これらのタイプの問題を回避する最も簡単な方法は、タイプについて明示することです。 SQL Serverは CAST and CONVERT functions この目的のため。文字列と日付/時刻型を扱う場合、CONVERTは、文字列形式を明示的に定義するスタイルパラメータを提供するため、推奨されます。

この質問では、ODBC canonical(with milliseconds))形式(スタイル121)の文字列を使用しています。データ型と文字列スタイルを明示すると、次のようになります。

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    CONVERT(datetime, '2014-11-01 00:00:00.000', 121)
    AND 
    CONVERT(datetime, '2014-11-30 23:59:59.997', 121);

そうは言っても、 正当な理由 (アーロンが 彼の答え で指摘しているように)BETWEENの代わりに半開範囲を使用する(私はスタイル120を使用します)以下は、多様性のためだけです:

SELECT COUNT(*)
FROM dbo.profile 
WHERE
    [Created] >= CONVERT(datetime, '2014-11-01 00:00:00', 120)
    AND [Created] < CONVERT(datetime, '2014-12-01 00:00:00', 120);

型について明示的にすることは、特に日付と時刻を処理する場合、非常に良い習慣になります。

8
Paul White 9

BETWEENは、さまざまな日付/時刻型の丸めやその他の問題のために非常に問題があります 、および YYYY-MM-DDは、厄介なT なしでは安全な形式ではありません。ISO標準完全な日付を使用した無制限の範囲セパレータははるかに良いアプローチです:

WHERE Created >= '20141101' AND Created < '20141201';
7
Aaron Bertrand