sql >> Databáze >  >> RDS >> Sqlserver

Jak převést varchar na datum, pouze když obsahuje platné datum?

Typickou odpovědí je přidat klauzuli WHERE:

WHERE ISDATE(a.valor) = 1

To je však ve vaší situaci problematické z několika důvodů:

  1. ISDATE() nemusí nutně odpovídat požadovanému způsobu v závislosti na regionálním nastavení serveru, jazyku uživatele nebo možnostech formátu data atd. Například:

    SET DATEFORMAT dmy;
    SELECT ISDATE('13/01/2012'); -- 1
    
    SET DATEFORMAT mdy;
    SELECT ISDATE('13/01/2012'); -- 0
    
  2. Nemůžete skutečně ovlivnit, že se SQL Server pokusí provést CONVERT po filtru.

Nemůžete ani použít poddotazy nebo CTE k pokusu oddělit filtr od CONVERT, protože SQL Server může optimalizovat operace v dotazu v jakémkoli pořadí, které považuje za efektivnější.

Například u omezeného vzorku pravděpodobně zjistíte, že to funguje dobře:

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 i s touto konstrukcí jsem viděl případy, kdy se SQL Server pokusil nejprve vyhodnotit filtr, což vedlo ke stejné chybě, jakou aktuálně dostáváte.

Několik bezpečnějších řešení:

Přidejte vypočítaný sloupec, např.

ALTER TABLE dbo.mytable ADD valor_date
  AS CONVERT(DATE, CASE WHEN ISDATE(valor) = 1 THEN valor 
    ELSE NULL END, 103);

Abyste se ochránili před možnými nesprávnými interpretacemi za běhu, měli byste před zadáním dotazu, který odkazuje na vypočítaný sloupec, zadat dateformat, např.

SET DATEFORMAT dmy;
SELECT valor, valor_date FROM dbo.mytable WHERE ...;

Vytvořte zobrazení:

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;

Opět budete chtít vydat SET DATEFORMAT při dotazu na zobrazení.

Použijte časovou tabulku:

SELECT <cols>
INTO #foo
FROM dbo.mytable
WHERE ISDATE(valor) = 1;

SELECT <cols>, CONVERT(DATE, valor) FROM #foo WHERE ...;

Můžete stále chtít použít DATEFORMAT abyste se ochránili před konflikty mezi ISDATE a uživatelská nastavení.

A ne, neměli byste zkuste ověřit své řetězce jako data pomocí shody vzorů řetězců, jak bylo navrženo v jiné (nyní smazané) odpovědi:

like '%__/%' or like '%/%'

Pro zpracování všech platných dat včetně přestupných let budete muset mít poměrně složité a náročné ověření.



  1. unserializovat problém

  2. Konfigurace Nginx s Magento 1.8

  3. Převod data v MySQL z pole řetězce

  4. Upgradováno na Ubuntu 16.04, nyní jsou přerušeny závislosti MySQL-python