Frage

Ich habe eine große Tabelle mit mehr als 1 Million Datensätzen.Leider hat die Person, die die Tabelle erstellt hat, beschlossen, Datumsangaben in eine Tabelle einzutragen varchar(50) Feld.

Ich muss einen einfachen Datumsvergleich durchführen -

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

Aber es scheitert an der convert():

Conversion failed when converting datetime from character string.

Anscheinend gibt es etwas in diesem Bereich, das ihm nicht gefällt, und da es so viele Aufzeichnungen gibt, kann ich es nicht anhand des bloßen Anblicks erkennen.Wie kann ich das gesamte Datumsfeld ordnungsgemäß bereinigen, damit es nicht fehlschlägt? convert()?Folgendes habe ich jetzt:

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

@SQLMenace

Ich mache mir in diesem Fall keine Sorgen um die Leistung.Dies wird eine einmalige Abfrage sein.Das Ändern der Tabelle in ein Datum/Uhrzeit-Feld ist keine Option.

@Jon Limjap

Ich habe versucht, das dritte Argument hinzuzufügen, aber es macht keinen Unterschied.


@SQLMenace

Das Problem liegt höchstwahrscheinlich darin, wie die Daten gespeichert werden. Es gibt nur zwei sichere Formate;ISO JJJJMMTT;ISO 8601 jjjj-mm-tt Thh:mm:ss:mmm (keine Leerzeichen)

Wäre das nicht isdate() Überprüfen Sie, ob Sie sich darum kümmern?

Ich brauche keine hundertprozentige Genauigkeit.Ich möchte nur die meisten Datensätze der letzten 30 Tage abrufen.


@SQLMenace

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

@Brian Schkerke

Platzieren Sie CASE und ISDATE in der Funktion CONVERT().

Danke!Das hat es geschafft.

War es hilfreich?

Lösung

Setze das CASE Und ISDATE im Inneren CONVERT() Funktion.

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

Ersetzen '12-30-1899' mit dem Standarddatum Ihrer Wahl.

Andere Tipps

Wie wäre es, einen Cursor zu schreiben, der den Inhalt durchläuft und die Umwandlung für jeden Eintrag versucht? Wenn ein Fehler auftritt, geben Sie den Primärschlüssel oder andere identifizierende Details für den Problemdatensatz aus.Ich kann mir keinen satzbasierten Weg vorstellen, dies zu tun.

Nicht vollständig satzbasiert, aber wenn nur 3 Zeilen von 1 Million fehlerhaft sind, sparen Sie viel Zeit

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

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

Dann schauen Sie sich einfach die BadDates-Tabelle an und beheben Sie das Problem

ISDATE() würde sich um die Zeilen kümmern, die nicht richtig formatiert wurden, wenn es tatsächlich zuerst ausgeführt würde.Wenn Sie sich jedoch den Ausführungsplan ansehen, werden Sie wahrscheinlich feststellen, dass das DATEDIFF-Prädikat zuerst angewendet wird – und somit die Ursache für Ihren Schmerz ist.

Wenn Sie SQL Server Management Studio verwenden, klicken Sie auf STRG+L um den geschätzten Ausführungsplan für eine bestimmte Abfrage anzuzeigen.

Denken Sie daran, dass SQL keine prozedurale Sprache ist und Kurzschlusslogik funktionieren kann, aber nur, wenn Sie sie sorgfältig anwenden.

Wie wäre es, wenn Sie einen Cursor schreiben, der den Inhalt durchläuft und die Umwandlung für jeden Eintrag versucht?

Wenn ein Fehler auftritt, geben Sie den Primärschlüssel oder andere identifizierende Details für den Problemdatensatz aus.

Ich kann mir keinen satzbasierten Weg vorstellen, dies zu tun.

Bearbeiten - Ach ja, ich habe ISDATE() vergessen.Auf jeden Fall ein besserer Ansatz als die Verwendung eines Cursors.+1 für SQLMenace.

In Ihrem Convert-Aufruf müssen Sie einen dritten Stilparameter angeben, z. B. das Format der Datums- und Uhrzeitangaben, die als Varchar gespeichert werden, wie in diesem Dokument angegeben: CAST und CONVERT (T-SQL)

Drucken Sie die Aufzeichnungen aus.Geben Sie den Ausdruck dem Idioten, der sich für die Verwendung von varchar(50) entschieden hat, und bitten Sie ihn, den Problemdatensatz zu finden.

Beim nächsten Mal sehen sie vielleicht einfach, welchen Sinn es hat, einen geeigneten Datentyp auszuwählen.

Das Problem liegt höchstwahrscheinlich darin, wie die Daten gespeichert werden. Es gibt nur zwei sichere Formate

ISO JJJJMMTT

ISO 8601 jjjj-mm-tt Thh:mm:ss:mmm (keine Leerzeichen)

Diese funktionieren unabhängig von Ihrer Sprache.

Möglicherweise müssen Sie SET DATEFORMAT YMD (oder was auch immer die Daten gespeichert sind) ausführen, damit es funktioniert

Würde sich die isdate()-Prüfung nicht darum kümmern?

Führen Sie dies aus, um zu sehen, was passiert

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

Ich bin mir sicher, dass das Ändern der Tabelle/Spalte aufgrund älterer Systemanforderungen möglicherweise keine Option ist. Haben Sie jedoch darüber nachgedacht, eine Ansicht mit integrierter Datumskonvertierungslogik zu erstellen, wenn Sie eine neuere Version von SQL verwenden? Können Sie möglicherweise sogar eine indizierte Ansicht verwenden?

Ich würde vorschlagen, das Chaos zu beseitigen und die Spalte in eine Datum/Uhrzeit-Spalte zu ändern, weil ich so etwas mache

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

kann keinen Index verwenden und ist um ein Vielfaches langsamer, als wenn Sie eine Datetime-Spalte hätten und dies getan hätten

where lastUpdate > getDate() -31

Natürlich müssen Sie auch Stunden und Sekunden berücksichtigen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top