典型的な答えは、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
CONVERT
SQL Server がフィルターの後に実行しようとすることを実際に制御することはできません。
サブクエリや CTE を使用して、フィルターを CONVERT から分離しようとすることさえできません。これは、SQL Serverがより効率的と思われる順序でクエリ内の操作を最適化できるためです。
たとえば、サンプルが限られている場合、おそらくこれで問題なく動作することがわかります。
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
ここでも、ビューを照会するときにa を発行する必要があります。
一時テーブルを使用します。
SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;
とユーザー設定DATEFORMAT
の間の競合から身を守るために、引き続き使用したい場合があります。ISDATE
いいえ、別の(現在は削除された)回答で提案されているように、文字列パターン マッチングを使用して文字列を日付として検証しようとしないでください。
like '%__/%' or like '%/%'
うるう年を含むすべての有効な日付を処理するには、かなり複雑で手間のかかる検証が必要です。