Det typiska svaret är att lägga till en WHERE-sats:
WHERE ISDATE(a.valor) = 1
Detta är dock problematiskt i din situation av ett par anledningar:
-
ISDATE()
kommer inte nödvändigtvis att matcha som du vill beroende på regionala inställningar på servern, användarens språk- eller datumformatalternativ, etc. Till exempel:SET DATEFORMAT dmy; SELECT ISDATE('13/01/2012'); -- 1 SET DATEFORMAT mdy; SELECT ISDATE('13/01/2012'); -- 0
-
Du kan inte riktigt kontrollera att SQL Server kommer att försöka utföra
CONVERT
efter filtret.
Du kan inte ens använda underfrågor eller CTE för att försöka separera filtret från CONVERT eftersom SQL Server kan optimera operationerna i frågan i vilken ordning den än anser vara effektivare.
Till exempel, med ett begränsat urval, kommer du förmodligen att upptäcka att detta fungerar okej:
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';
Men jag har sett fall med även den här konstruktionen där SQL Server har försökt utvärdera filtret först, vilket leder till samma fel som du får för närvarande.
Ett par säkrare lösningar:
Lägg till en beräknad kolumn, t.ex.
ALTER TABLE dbo.mytable ADD valor_date
AS CONVERT(DATE, CASE WHEN ISDATE(valor) = 1 THEN valor
ELSE NULL END, 103);
För att skydda dig mot möjliga feltolkningar vid körning bör du ange datumformat innan du skickar en fråga som refererar till den beräknade kolumnen, t.ex.
SET DATEFORMAT dmy;
SELECT valor, valor_date FROM dbo.mytable WHERE ...;
Skapa en vy:
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;
Återigen, du vill utfärda en SET DATEFORMAT
när du frågar vyn.
Använd en temptabell:
SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;
Du kanske fortfarande vill använda DATEFORMAT
för att skydda dig från konflikter mellan ISDATE
och användarinställningar.
Och nej, du borde inte försök att validera dina strängar som datum med strängmönstermatchning som föreslogs i ett annat (nu borttaget) svar:
like '%__/%' or like '%/%'
Du måste ha en ganska komplicerad och hårdhänt validering där för att hantera alla giltiga datum inklusive skottår.