Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Jak przekonwertować varchar na datę tylko wtedy, gdy zawiera poprawną datę?

Typową odpowiedzią jest dodanie klauzuli WHERE:

WHERE ISDATE(a.valor) = 1

Jest to jednak problematyczne w Twojej sytuacji z kilku powodów:

  1. 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
    
  2. 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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Zapytania heterogeniczne wymagają ustawienia opcji ANSI_NULLS i ANSI_WARNINGS dla połączenia. Zapewnia to spójną semantykę zapytań

  2. SQL Server In-Memory OLTP:podstawy

  3. Jak dodać numery linii w SQL Server Management Studio (SSMS) — samouczek SQL Server / TSQL, część 11

  4. Błąd:nie można użyć widoku lub funkcji z powodu błędów wiązania

  5. Czy istnieje błąd w SqlDataReader.HasRows podczas uruchamiania programu SQL Server 2008?