Vergleichen von Datum und Uhrzeit mit nur Datum in SQL Server


77
Select * from [User] U
where  U.DateCreated = '2014-02-07'     

aber in der Datenbank wurde der Benutzer erstellt 2014-02-07 12:30:47.220und als ich nur legte'2014-02-07'

Es werden keine Daten angezeigt

Antworten:


81

Seien Sie nicht versucht, solche Dinge zu tun:

Select * from [User] U where convert(varchar(10),U.DateCreated, 120) = '2014-02-07'

Dies ist ein besserer Weg:

Select * from [User] U 
where U.DateCreated >= '2014-02-07' and U.DateCreated < dateadd(day,1,'2014-02-07')

siehe: Was bedeutet das Wort "SARGable" wirklich?

BEARBEITEN + Es gibt zwei grundlegende Gründe, um die Verwendung von Funktionen für Daten in der where-Klausel (oder in Join-Bedingungen) zu vermeiden.

  1. In den meisten Fällen wird durch die Verwendung einer Funktion für Daten zum Filtern oder Verknüpfen die Möglichkeit des Optimierers aufgehoben, auf einen Index für dieses Feld zuzugreifen, wodurch die Abfrage langsamer (oder "kostspieliger") wird.
  2. Die andere ist, dass für jede betroffene Datenzeile mindestens eine Berechnung durchgeführt wird. Das könnte bedeuten, dass der Abfrage Hunderte, Tausende oder viele Millionen Berechnungen hinzugefügt werden, damit wir sie mit einem einzelnen Kriterium wie vergleichen können 2014-02-07. Es ist weitaus effizienter, die Kriterien an die Daten anzupassen.

"Ändern der Kriterien an die Daten" ist meine Art, " SARGABLEPrädikate verwenden" zu beschreiben.


Und nicht zwischen verwenden.

Die beste Vorgehensweise bei Datums- und Zeitbereichen besteht darin, ZWISCHEN zu vermeiden und immer das folgende Formular zu verwenden:

WHERE col> = '20120101' AND col <'20120201' Dieses Formular funktioniert mit allen Typen und Präzisionen, unabhängig davon, ob der Zeitteil anwendbar ist.

http://sqlmag.com/t-sql/t-sql-best-practices-part-2 (Itzik Ben-Gan)


2
Wenn Sie etwas Zeit haben, sollten Sie erklären, warum einer besser ist als der andere (Indexverwendung), das macht dies zu einer besseren Antwort
Serpiton

leichter gesagt als getan, es gibt verschiedene Datentypen (Datum, Datum / Uhrzeit, Datum / Uhrzeit2, kleine Datenzeit) sowie verschiedene MSSQL-Versionen, die berücksichtigt werden müssen. Eine Antwort passt leider nicht zu allem, was ich fürchte. Aber ich werde eine Notiz hinzufügen.
Used_By_Already

1
Wenn Sie cast verwenden (Datum / Uhrzeit als Datum), wird weiterhin ein Index für das Feld Datum / Uhrzeit verwendet. Versuch es.
Steve Ford

@ Steve Ford, ich stimme zu - in letzteren Versionen von mssql -, warum ich sagte "es ist leichter gesagt als getan"; Es ist sehr schwer, für alle Bedingungen richtig zu sein. Es ist jedoch eine gute Praxis, sargable Prädikate zu verwenden, auch wenn Optimierer dies canmanchmal kompensieren.
Used_By_Already

2
Das sicherste date literalFormat ist "JJJJMMTT". Datentypen: Datum oder Datum / Uhrzeit oder Uhrzeit oder Datum / Uhrzeit2 oder kleine Datenzeit haben KEIN Format, da sie als Zahlen gespeichert sind. Für Datumsliterale ist 'JJJJ-MM-TT' NICHT ganz sicher. Es gibt eine Gebietsschemaeinstellung, die diese Sequenz als JJJJ-TT-MM falsch interpretieren kann
Used_By_Already

69

Wenn Sie mit SQL Server 2008 oder höher arbeiten, können Sie den Datumsdatentyp verwenden:

SELECT *
FROM [User] U
WHERE CAST(U.DateCreated as DATE) = '2014-02-07'

Es ist zu beachten, dass bei einer Indizierung der Datumsspalte der Index weiterhin verwendet wird und SARGable ist. Dies ist ein Sonderfall für Datums- und Uhrzeitangaben.

Geben Sie hier die Bildbeschreibung ein

Sie können sehen, dass SQL Server dies tatsächlich in eine> und <-Klausel umwandelt:

Geben Sie hier die Bildbeschreibung ein

Ich habe dies gerade an einer großen Tabelle versucht, mit einem Sekundärindex in der Datumsspalte gemäß den Kommentaren von @ kobik, und der Index wird weiterhin verwendet. Dies ist bei den Beispielen, die ZWISCHEN oder> = und <: verwenden, nicht der Fall.

SELECT *
FROM [User] U
WHERE CAST(U.DateCreated as DATE) = '2016-07-05'

Anzeigen der Indexnutzung mit Sekundärindex


Es scheint, als hätte ich diese Antwort ins Visier genommen. Dies war nicht der Fall, da wir fast zur gleichen Zeit gepostet haben und ich Ihre Antwort nicht kannte. Aber um die Notwendigkeit zu verringern, dass Sie Ihr Bild wieder vergrößern, habe ich mein Beispiel "Nicht tun" geändert. Vielleicht hilft dies?
Used_By_Already

Wie läuft die AaronsBadDatenbank heutzutage?
Pure.Krome

@ Pure.Krome, wie Sie sehen können, war eine temporäre Datenbank, die nicht mehr existiert.
Steve Ford

"Es sollte beachtet werden, dass, wenn die Datumsspalte indiziert ist, der Index weiterhin verwendet wird und SARGable ist" - Dies gilt, wenn DateCreated PK oder Clustered Index ist. Für nicht gruppierte Indizes gibt es einen vollständigen Tabellenscan.
Kobik

@kobik in der Tat habe ich gerade einen Test durchgeführt und diese Lösung verwendet einen Index für eine große Tabelle. Die anderen Lösungen nicht.
Steve Ford

3

Nach Ihrer Anfrage Select * from [User] U where U.DateCreated = '2014-02-07'

SQL Server vergleicht genaues Datum und die Zeit , dh ( im Vergleich 2014-02-07 12:30:47.220mit 2014-02-07 00:00:00.000der Gleichheit). Deshalb ist das Vergleichsergebnis falsch

Daher müssen Sie beim Vergleichen von Daten auch die Zeit berücksichtigen. Sie können verwenden
Select * from [User] U where U.DateCreated BETWEEN '2014-02-07' AND '2014-02-08'.


2
Leider definiert SQL betweenmit Gleichheit an beiden Enden (> = und <=), z. B. select * from x where col_1 between 'A' and 'M' enthält A und M (plus B bis L natürlich). Diese inklusive Natur von betweenist großartig für so etwas wie einen Dokumentenindex; Für Datums- / Zeitbereiche ist dies jedoch eine Katastrophe, da es zu Überlappungen kommt. Ich weiß, dass die Verwendung von zwischen gut aussieht, aber in Wahrheit SQL betweenfunktioniert ein einfach nicht richtig für Datumsbereiche. Verwenden Sie immer eine Kombination von> = mit <wie inU.DateCreated >='2014-02-07' and U.DateCreated < '2014-02-08'
Used_By_Already

@Used_By_Already Stimmen Sie zu, aber für diesen speziellen Fall (in Frage) betweenist OK. Ihr Vorschlag zu verwenden >=und <ist zweifellos betweenfür Datumsbereiche vorzuziehen . Ich möchte nur sagen, dass wir die Zeit auch beim Vergleichen von Daten berücksichtigen sollten.
Imdzeeshan

0

Natürlich ist dies ein alter Thread, aber um ihn zu vervollständigen.

Ab SQL 2008 können Sie den Datentyp DATE verwenden, um Folgendes zu tun:

SELECT CONVERT(DATE,GETDATE())

OR

Select * from [User] U
where  CONVERT(DATE,U.DateCreated) = '2014-02-07' 

-2

Bitte versuchen Sie dies. Diese Abfrage kann zum Datumsvergleich verwendet werden

select * from [User] U where convert(varchar(10),U.DateCreated, 120) = '2014-02-07'

-3

Sie können LIKEanstelle von Anweisung verwenden =. Aber um dies mit DateStamp zu tun, müssen Sie CONVERTes zuerst zu VARCHAR:

SELECT * 
FROM [User] U
WHERE CONVERT(VARCHAR, U.DateCreated, 120) LIKE '2014-02-07%'

1
+1 Da dies eine funktionierende Lösung ist und in vielen Szenarien verwendet werden kann, stimmen Sie funktionierende Antworten nicht ab, nur weil Sie der Meinung sind, dass dies keine gute Praxis ist. Stellen Sie uns einen zuverlässigen Artikel oder ein Dokument zur Verfügung, und überzeugen Sie uns dann mehr
und

3
@Ahmad String-Konvertierung gefolgt von Vergleich ist teurer als Datumsvergleich. Die Abstimmungen sind also gerechtfertigt, da dies keine gute Möglichkeit ist, das zu tun, was das OP tun möchte.
mbomb007
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.