sql >> Database teknologi >  >> RDS >> Sqlserver

Hvordan konverterer man kun varchar til dato, når den indeholder en gyldig dato?

Det typiske svar er at tilføje en WHERE-sætning:

WHERE ISDATE(a.valor) = 1

Dette er dog problematisk i din situation af et par grunde:

  1. ISDATE() vil ikke nødvendigvis matche den måde, du ønsker, afhængigt af regionale indstillinger på serveren, brugerens sprog- eller datoformatindstillinger osv. For eksempel:

    SET DATEFORMAT dmy;
    SELECT ISDATE('13/01/2012'); -- 1
    
    SET DATEFORMAT mdy;
    SELECT ISDATE('13/01/2012'); -- 0
    
  2. Du kan ikke rigtig kontrollere, at SQL Server vil forsøge at udføre CONVERT efter filteret.

Du kan ikke engang bruge underforespørgsler eller CTE'er til at prøve at adskille filteret fra CONVERT, fordi SQL Server kan optimere operationerne i forespørgslen i den rækkefølge, den anser for mere effektiv.

For eksempel, med en begrænset prøve, vil du sandsynligvis opdage, at dette fungerer okay:

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

Men jeg har set tilfælde med selv denne konstruktion, hvor SQL Server har forsøgt at evaluere filteret først, hvilket fører til den samme fejl, som du får i øjeblikket.

Et par mere sikre løsninger:

Tilføj en beregnet kolonne, f.eks.

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

For at beskytte dig selv mod mulige fejlfortolkninger under kørsel, bør du angive datoformat, før du udsteder en forespørgsel, der refererer til den beregnede kolonne, f.eks.

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

Opret en visning:

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;

Igen skal du udstede en SET DATEFORMAT når du forespørger på visningen.

Brug en midlertidig tabel:

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

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

Du vil muligvis stadig bruge DATEFORMAT for at beskytte dig selv mod konflikter mellem ISDATE og brugerindstillinger.

Og nej, du skal ikke prøv at validere dine strenge som datoer ved hjælp af strengmønstermatchning som blev foreslået i et andet (nu slettet) svar:

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

Du bliver nødt til at have en ret kompleks og hårdhændet validering der for at håndtere alle gyldige datoer inklusive skudår.



  1. Kommandohistorie i isql

  2. NAME_IN indbygget i Oracle D2k Forms

  3. Hvad dækker indekser og dækkede forespørgsler i SQL Server?

  4. Indekserer du en MySql TEXT kolonne?