Typową odpowiedzią jest dodanie klauzuli WHERE:
WHERE ISDATE(a.valor) = 1
Jest to jednak problematyczne w Twojej sytuacji z kilku powodów:
-
ISDATE()
niekoniecznie będzie pasować tak, jak chcesz, w zależności od ustawień regionalnych serwera, języka użytkownika lub opcji formatu daty itp. Na przykład:SET DATEFORMAT dmy; SELECT ISDATE('13/01/2012'); -- 1 SET DATEFORMAT mdy; SELECT ISDATE('13/01/2012'); -- 0
-
Tak naprawdę nie możesz kontrolować, czy SQL Server spróbuje wykonać
CONVERT
za filtrem.
Nie możesz nawet użyć podzapytań lub CTE, aby spróbować oddzielić filtr od CONVERT, ponieważ SQL Server może zoptymalizuj operacje w zapytaniu w dowolnej kolejności, którą uzna za bardziej wydajną.
Na przykład przy ograniczonej próbce prawdopodobnie okaże się, że to działa dobrze:
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';
Ale widziałem przypadki nawet z tą konstrukcją, w których SQL Server próbował najpierw ocenić filtr, co prowadziło do tego samego błędu, który obecnie otrzymujesz.
Kilka bezpieczniejszych obejść:
Dodaj kolumnę obliczeniową, np.
ALTER TABLE dbo.mytable ADD valor_date
AS CONVERT(DATE, CASE WHEN ISDATE(valor) = 1 THEN valor
ELSE NULL END, 103);
Aby zabezpieczyć się przed możliwymi błędnymi interpretacjami w czasie wykonywania, należy określić format daty przed wysłaniem zapytania, które odwołuje się do obliczonej kolumny, np.
SET DATEFORMAT dmy;
SELECT valor, valor_date FROM dbo.mytable WHERE ...;
Utwórz widok:
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;
Ponownie, będziesz chciał wydać SET DATEFORMAT
podczas zapytania o widok.
Użyj tabeli tymczasowej:
SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;
SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;
Nadal możesz chcieć użyć DATEFORMAT
aby uchronić się przed konfliktami między ISDATE
i ustawienia użytkownika.
I nie, nie spróbuj zweryfikować ciągi jako daty, używając dopasowania wzorca ciągu, jak sugerowano w innej (teraz usuniętej) odpowiedzi:
like '%__/%' or like '%/%'
Będziesz musiał mieć dość złożoną i ciężką walidację, aby obsłużyć wszystkie ważne daty, w tym lata przestępne.