sql >> Database >  >> RDS >> Sqlserver

Hoe varchar alleen naar datum te converteren als het een geldige datum bevat?

Het typische antwoord is om een ​​WHERE-clausule toe te voegen:

WHERE ISDATE(a.valor) = 1

Dit is echter om een ​​aantal redenen problematisch in uw situatie:

  1. ISDATE() komt niet noodzakelijk overeen met de manier waarop u wilt, afhankelijk van de regionale instellingen van de server, de taal van de gebruiker of de datumnotatie-opties, enz. Bijvoorbeeld:

    SET DATEFORMAT dmy;
    SELECT ISDATE('13/01/2012'); -- 1
    
    SET DATEFORMAT mdy;
    SELECT ISDATE('13/01/2012'); -- 0
    
  2. Je kunt niet echt controleren of SQL Server zal proberen de CONVERT . uit te voeren na het filter.

U kunt zelfs geen subquery's of CTE's gebruiken om te proberen het filter van de CONVERT te scheiden, omdat SQL Server kan optimaliseer de bewerkingen in de query in elke volgorde die het efficiënter acht.

Met een beperkte steekproef zul je bijvoorbeeld waarschijnlijk merken dat dit goed werkt:

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';

Maar ik heb gevallen gezien met zelfs deze constructie waarbij SQL Server eerst heeft geprobeerd het filter te evalueren, wat leidde tot dezelfde fout die u momenteel krijgt.

Een paar veiligere oplossingen:

Voeg een berekende kolom toe, bijv.

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

Om uzelf te beschermen tegen mogelijke verkeerde interpretaties tijdens runtime, moet u het datumformaat specificeren voordat u een query opgeeft die verwijst naar de berekende kolom, bijvoorbeeld

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

Maak een weergave:

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;

Nogmaals, u wilt een SET DATEFORMAT . uitgeven bij het opvragen van de weergave.

Gebruik een tijdelijke tabel:

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

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

Misschien wilt u nog steeds DATEFORMAT . gebruiken om uzelf te beschermen tegen conflicten tussen ISDATE en gebruikersinstellingen.

En nee, u moet niet probeer uw strings te valideren als datums met behulp van stringpatroonovereenkomst zoals werd gesuggereerd in een ander (nu verwijderd) antwoord:

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

Je zult daar een behoorlijk complexe en hardhandige validatie moeten hebben om alle geldige datums, inclusief schrikkeljaren, te verwerken.



  1. SQL meerdere kolommen in IN-clausule

  2. Wat kan het Queryplan vertellen?

  3. Uitzondering bestand niet gevonden bij het uploaden van een bestand naar de xampp-server vanuit de Android-applicatie

  4. Windows 7 PHP MySQL-verbindingsproblemen