Типичният отговор е да добавите клауза 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
-
Не можете наистина да контролирате дали SQL Server ще се опита да изпълни
CONVERT
след филтъра.
Не можете дори да използвате подзаявки или 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
при запитване към изгледа.
Използвайте временна таблица:
SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;
Все още може да искате да използвате DATEFORMAT
за да се предпазите от конфликти между ISDATE
и потребителски настройки.
И не, не трябва опитайте да потвърдите вашите низове като дати, като използвате съвпадение на низови шаблони, както беше предложено в друг (сега изтрит) отговор:
like '%__/%' or like '%/%'
Ще трябва да имате някаква доста сложна и тежка проверка там, за да обработите всички валидни дати, включително високосни години.