Question

J'ai une grande table avec plus d'un million d'enregistrements.Malheureusement, la personne qui a créé le tableau a décidé de mettre les dates dans un varchar(50) champ.

Je dois faire une simple comparaison de dates -

datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

Mais cela échoue sur le convert():

Conversion failed when converting datetime from character string.

Apparemment, il y a quelque chose dans ce domaine qui ne lui plaît pas, et comme il y a tellement de documents, je ne peux pas le dire simplement en les regardant.Comment puis-je nettoyer correctement l'intégralité du champ de date afin qu'il n'échoue pas sur le convert()?Voici ce que j'ai maintenant :

select count(*)
from MyTable
where
    isdate(lastUpdate) > 0
    and datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

@SQLMenace

Je ne suis pas préoccupé par les performances dans ce cas.Cela va être une requête unique.Changer la table en champ datetime n'est pas une option.

@Jon Limjap

J'ai essayé d'ajouter le troisième argument, et cela ne fait aucune différence.


@SQLMenace

Le problème réside probablement dans la manière dont les données sont stockées. Il n’existe que deux formats sécurisés :ISO AAAAMMJJ ;ISO 8601 aaaa-mm-jj Thh:mm:ss:mmm (sans espaces)

Ne serait-ce pas le isdate() vérifie, prends soin de ça ?

Je n'ai pas besoin d'une précision à 100%.Je veux juste obtenir la plupart des enregistrements des 30 derniers jours.


@SQLMenace

select isdate('20080131') -- returns 1
select isdate('01312008') -- returns 0

@Brian Schkerke

Placez le CASE et l'ISDATE dans la fonction CONVERT().

Merci!Cela l'a fait.

Était-ce utile?

La solution

Placer le CASE et ISDATE à l'intérieur de CONVERT() fonction.

SELECT COUNT(*) FROM MyTable
WHERE
    DATEDIFF(dd, CONVERT(DATETIME, CASE IsDate(lastUpdate)
        WHEN 1 THEN lastUpdate
        ELSE '12-30-1899'
    END), GetDate()) < 31

Remplacer '12-30-1899' avec la date par défaut de votre choix.

Autres conseils

Que diriez-vous d'écrire un curseur pour parcourir le contenu, en essayant la conversion pour chaque entrée ? Lorsqu'une erreur se produit, affichez la clé primaire ou d'autres détails d'identification pour l'enregistrement problématique.Je ne peux pas penser à une manière basée sur un ensemble de procéder.

Pas totalement défini, mais si seulement 3 lignes sur 1 million sont mauvaises, cela vous fera gagner beaucoup de temps

select * into BadDates
from Yourtable
where isdate(lastUpdate) = 0

select * into GoodDates
from Yourtable
where isdate(lastUpdate) = 1

alors regardez simplement la table BadDates et corrigez cela

ISDATE() s'occuperait des lignes qui n'étaient pas formatées correctement si elle était effectivement exécutée en premier.Cependant, si vous examinez le plan d'exécution, vous constaterez probablement que le prédicat DATEDIFF est appliqué en premier, ce qui constitue la cause de votre douleur.

Si vous utilisez SQL Server Management Studio, cliquez sur CTRL+L pour afficher le plan d'exécution estimé pour une requête particulière.

N'oubliez pas que SQL n'est pas un langage procédural et que la logique de court-circuit peut fonctionner, mais seulement si vous faites attention à la manière dont vous l'appliquez.

Que diriez-vous d'écrire un curseur pour parcourir le contenu, en essayant le cast pour chaque entrée ?

Lorsqu'une erreur se produit, affichez la clé primaire ou d'autres détails d'identification pour l'enregistrement du problème.

Je ne peux pas penser à une manière basée sur un ensemble de procéder.

Modifier - ah oui, j'ai oublié ISDATE().Certainement une meilleure approche que d’utiliser un curseur.+1 à SQLMenace.

Dans votre appel de conversion, vous devez spécifier un troisième paramètre de style, par exemple le format des dates et heures stockées sous forme de varchar, comme spécifié dans ce document : CAST et CONVERT (T-SQL)

Imprimez les enregistrements.Donnez la copie papier à l'idiot qui a décidé d'utiliser un varchar(50) et demandez-lui de trouver l'enregistrement problématique.

La prochaine fois, ils comprendront peut-être l’intérêt de choisir un type de données approprié.

Le problème vient probablement de la façon dont les données sont stockées, il n'existe que deux formats sécurisés

ISO AAAAMMJJ

ISO 8601 aaaa-mm-jj Thh:mm:ss:mmm (sans espaces)

ceux-ci fonctionneront quelle que soit votre langue.

Vous devrez peut-être effectuer un SET DATEFORMAT YMD (ou quel que soit le support sous lequel les données sont stockées) pour que cela fonctionne.

La vérification isdate() ne s'occuperait-elle pas de cela ?

Exécutez ceci pour voir ce qui se passe

select isdate('20080131')
select isdate('01312008')

Je suis sûr que changer la table/colonne n'est peut-être pas une option en raison des exigences système existantes, mais avez-vous pensé à créer une vue intégrant la logique de conversion de date, si vous utilisez une version plus récente de SQL, alors vous pouvez éventuellement même utiliser une vue indexée ?

Je suggérerais de nettoyer le désordre et de changer la colonne en datetime parce que faire des choses comme ça

WHERE datediff(dd, convert(datetime, lastUpdate), getDate()) < 31

vous ne pouvez pas utiliser d'index et ce sera plusieurs fois plus lent que si vous aviez une colonne datetime,n et vous l'avez fait

where lastUpdate > getDate() -31

Il faut aussi prendre en compte les heures et les secondes bien sûr

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top