SQL Server の DateTime 変換エラー
-
09-06-2019 - |
質問
100 万件以上のレコードを含む大きなテーブルがあります。残念ながら、表を作成した人は、日付を varchar(50)
分野。
単純な日付比較を行う必要があります -
datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31
しかし、それは失敗します convert()
:
Conversion failed when converting datetime from character string.
どうやらその分野に苦手なものがあるようで、記録がたくさんあるので見ただけではわかりません。日付フィールド全体を適切にサニタイズして、 convert()
?私が今持っているものは次のとおりです。
select count(*)
from MyTable
where
isdate(lastUpdate) > 0
and datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31
この場合、パフォーマンスについては心配しません。これは 1 回限りのクエリになります。テーブルを日時フィールドに変更することはできません。
3番目の引数を追加してみましたが、違いはありません。
問題はおそらくデータの保存方法にあります。安全な形式は 2 つだけです。ISO YYYYMMDD;ISO 8601 yyyy-mm-dd Thh:mm:ss:mmm (スペースなし)
そうではありませんか isdate()
チェックしてください?
100%の精度は必要ありません。過去 30 日間のほとんどのレコードを取得したいだけです。
select isdate('20080131') -- returns 1
select isdate('01312008') -- returns 0
CASE と ISDATE を CONVERT() 関数内に配置します。
ありがとう!それでできました。
解決
を配置します。 CASE
そして ISDATE
の中で CONVERT()
関数。
SELECT COUNT(*) FROM MyTable
WHERE
DATEDIFF(dd, CONVERT(DATETIME, CASE IsDate(lastUpdate)
WHEN 1 THEN lastUpdate
ELSE '12-30-1899'
END), GetDate()) < 31
交換する '12-30-1899'
選択したデフォルトの日付を使用します。
他のヒント
コンテンツをループするカーソルを作成し、エントリごとにキャストを試みてはどうでしょうか?エラーが発生した場合は、問題レコードの主キーまたはその他の識別詳細を出力します。これを行うためのセットベースの方法は思いつきません。
完全にセットベースではありませんが、100 万行のうち 3 行だけが不良であれば、大幅な時間を節約できます。
select * into BadDates
from Yourtable
where isdate(lastUpdate) = 0
select * into GoodDates
from Yourtable
where isdate(lastUpdate) = 1
次に、BadDates テーブルを確認して修正します。
ISDATE() が最初に実行されていた場合、適切にフォーマットされていない行は ISDATE() によって処理されます。ただし、実行計画を見ると、おそらく DATEDIFF 述語が最初に適用されていることがわかります。これが問題の原因です。
SQL Server Management Studio を使用している場合は、 CTRL+L 特定のクエリの推定実行計画を表示します。
SQL は手続き型言語ではないため、短絡ロジックは機能する可能性がありますが、それは適用方法に注意した場合に限られることに注意してください。
コンテンツをループするカーソルを作成し、各エントリのキャストを試みてみるのはどうでしょうか?
エラーが発生した場合は、問題レコードの主キーまたはその他の識別詳細を出力します。
これを行うためのセットベースの方法は思いつきません。
編集 - そうそう、ISDATE() のことを忘れていました。カーソルを使用するよりも確実に優れたアプローチです。SQLMenace に +1。
変換呼び出しでは、このドキュメントで指定されているように、3 番目のスタイル パラメーター (たとえば、varchar として保存される日時の形式) を指定する必要があります。 キャストと変換 (T-SQL)
記録を印刷します。varchar(50) を使用することに決めた愚か者にハードコピーを渡して、問題の記録を見つけるように依頼してください。
次回は、適切なデータ型を選択するポイントがわかるかもしれません。
問題はおそらくデータの保存方法にあり、安全な形式は 2 つだけです
ISO YYYYMMDD
ISO 8601 yyyy-mm-dd Thh:mm:ss:mmm(スペースなし)
これらは言語に関係なく機能します。
これを機能させるには、SET DATEFORMAT YMD (またはデータが保存されているもの) を実行する必要がある場合があります。
isdate() チェックはこれに対処しないでしょうか?
これを実行して何が起こるかを確認してください
select isdate('20080131')
select isdate('01312008')
従来のシステム要件により、テーブル/列の変更はオプションではないかもしれませんが、より新しいバージョンの SQL を使用している場合は、日付変換ロジックが組み込まれたビューを作成することを考えたことはありますか?インデックス付きビューを使用することもできますか?
このようなことを行うため、混乱をクリーンアップして列を日付時刻に変更することをお勧めします
WHERE datediff(dd, convert(datetime, lastUpdate), getDate()) < 31
インデックスを使用できないため、datetime 列 n を使用してインデックスを使用した場合よりも何倍も遅くなります。
where lastUpdate > getDate() -31
もちろん時間と秒も考慮する必要があります