典型的な答えは、WHERE句を追加することです:
WHERE ISDATE(a.valor) = 1
ただし、これはいくつかの理由で状況に問題があります。
-
ISDATE()
サーバーの地域設定、ユーザーの言語または日付形式のオプションなどによっては、必ずしも希望どおりに一致するとは限りません。例:SET DATEFORMAT dmy; SELECT ISDATE('13/01/2012'); -- 1 SET DATEFORMAT mdy; SELECT ISDATE('13/01/2012'); -- 0
-
SQLServerが
CONVERT
を実行しようとすることを実際に制御することはできません。 フィルターの後。
SQLServerはできるため、サブクエリやCTEを使用してフィルターをCONVERTから分離することもできません。 クエリの操作を、より効率的と思われる順序で最適化します。
たとえば、限られたサンプルでは、これで問題がないことがわかるでしょう:
SET DATEFORMAT dmy;
SELECT valor, valor_date FROM (
SELECT valor, valor_date = CONVERT(DATE,
CASE WHEN ISDATE(valor) = 1 THEN valor ELSE NULL END, 103)
FROM dbo.mytable
WHERE ISDATE(valor) = 1
) AS sub WHERE valor_date BETWEEN '01/01/2012' AND '01/03/2012';
しかし、SQL Serverが最初にフィルターを評価しようとして、現在発生しているのと同じエラーが発生する、この構成のケースも見ました。
より安全な回避策がいくつかあります:
計算列を追加します。例:
ALTER TABLE dbo.mytable ADD valor_date
AS CONVERT(DATE, CASE WHEN ISDATE(valor) = 1 THEN valor
ELSE NULL END, 103);
実行時に起こりうる誤解から身を守るために、計算列を参照するクエリを発行する前に、dateformatを指定する必要があります。例:
SET DATEFORMAT dmy;
SELECT valor, valor_date FROM dbo.mytable WHERE ...;
ビューを作成する:
CREATE VIEW dbo.myview
AS
SELECT valor, valor_date = CONVERT(DATE,
CASE WHEN ISDATE(valor) = 1 THEN valor ELSE NULL END, 103)
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
繰り返しになりますが、SET DATEFORMAT
を発行する必要があります ビューをクエリするとき。
一時テーブルを使用する:
SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;
それでもDATEFORMAT
を使用することをお勧めします ISDATE
間の競合から身を守るため およびユーザー設定。
いいえ、すべきではありません 別の(現在は削除されている)回答で提案されているように、文字列パターンマッチングを使用して、文字列を日付として検証してみてください:
like '%__/%' or like '%/%'
うるう年を含むすべての有効な日付を処理するには、かなり複雑で手間のかかる検証が必要になります。