Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Как да конвертирате varchar в дата само когато съдържа валидна дата?

Типичният отговор е да добавите клауза WHERE:

WHERE ISDATE(a.valor) = 1

Това обаче е проблематично във вашата ситуация поради няколко причини:

  1. ISDATE() няма непременно да съответства на начина, по който искате, в зависимост от регионалните настройки на сървъра, езика на потребителя или опциите за формат на датата и т.н. Например:

    SET DATEFORMAT dmy;
    SELECT ISDATE('13/01/2012'); -- 1
    
    SET DATEFORMAT mdy;
    SELECT ISDATE('13/01/2012'); -- 0
    
  2. Не можете наистина да контролирате дали 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 '%/%'

Ще трябва да имате някаква доста сложна и тежка проверка там, за да обработите всички валидни дати, включително високосни години.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Грешка в SQL Server Management Studio Запазеният файл с настройки не може да бъде намерен

  2. Преобразуване на низ в int в linq към обекти в голяма база данни

  3. PHP &SQL Server - съкратени имена на полета

  4. Как да предотвратите едновременното изпълнение на задания на Sql сървър

  5. Замяна на определен знак в имейл адреси с '*' в SQL заявка