Verwendung von DATEDIFF in T-SQL
Frage
Ich verwende DATEDIFF in einer SQL-Anweisung.Ich wähle es aus und muss es auch in der WHERE-Klausel verwenden.Diese Aussage funktioniert nicht...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10
Es gibt die Botschaft: Ungültiger Spaltenname „InitialSave“
Aber diese Aussage funktioniert gut ...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10
Der Programmierer in mir sagt, dass dies ineffizient ist (es scheint, als würde ich die Funktion zweimal aufrufen).
Also zwei Fragen.Warum funktioniert die erste Aussage nicht?Ist es ineffizient, dies mit der zweiten Anweisung zu tun?
Lösung
Sie können keine Spalten in der SELECT-Anweisung in der where-Anweisung definiert zugreifen, da sie bis nicht erzeugt ist, nachdem die in dem ausgeführt wird.
Sie können dies jedoch tun
select InitialSave from
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10
Als Nebenbei bemerkt - das bewegt sich im Wesentlichen die DATEDIFF in die where-Anweisung in Bezug auf, wo es zuerst definiert. Mit Funktionen auf Spalten in denen Aussagen verursachen Indizes nicht so effizient genutzt werden und sollen jedoch nach Möglichkeit vermieden werden, wenn Sie verwenden haben datediff dann haben Sie, es zu tun!
Andere Tipps
Hinweis: Wenn ich ursprünglich schrieb diese Antwort, die ich sagte, dass ein Index für eine der Spalten eine Abfrage erstellen können, die als andere Antworten zu besseren Ergebnissen führt (und erwähnte Dan Fuller). Allerdings war ich nicht richtig denken zu 100%. Tatsache ist, dass ohne eine berechnete Spalte oder indiziert (materialisiert) Ansicht, ein Full Table Scan sein wird, erforderlich , weil die beiden Datumsspalten verglichen werden, sind von der gleichen Tabelle!
Ich glaube, es immer noch Wert in den unten angegebenen Informationen, nämlich 1) die Möglichkeit einer verbesserten Leistung in der richtigen Situation, wie wenn der Vergleich zwischen den Säulen aus verschiedenen Tabellen ist, und 2) die Förderung die Gewohnheit in SQL-Entwickler von folgendem Best Praxis und Umformen ihres Denkens in der richtigen Richtung.
Herstellungsbedingungen sargable
Die beste Praxis Ich beziehe mich gehört eine Spalte zu bewegen allein auf einer Seite des Vergleichsoperators zu sein, etwa so:
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'
Wie gesagt, wird dies nicht einen Scan auf einer einzigen Tabelle vermeiden, aber wie dies in einer Situation, es einen großen Unterschied machen könnte:
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM
dbo.BeginTime B
INNER JOIN dbo.EndTime E
ON B.BeginTime <= E.EndTime
AND B.BeginTime + '00:00:10' > E.EndTime
ist EndTime
in beiden Bedingungen jetzt allein auf der einen Seite des Vergleichs. Unter der Annahme, dass die BeginTime
Tabelle viele weniger Zeilen hat, und die EndTime
Tabelle hat einen Index für Spalte EndTime
, wird solches DateDiff(second, B.BeginTime, E.EndTime)
weit, weit besser als alles, was verwendet wird. Es ist jetzt sargable , was bedeutet, ein gültiges ist "Suchargument" - so wie der Motor Scans der BeginTime
Tisch, kann es suchen in die EndTime
Tabelle. Eine sorgfältige Auswahl, welche Kolonne allein schon auf der einen Seite des Bedieners erforderlich ist - kann es sich lohnen, das Experimentieren von BeginTime
selbst setzen, indem Sie einige Algebra zu tun wechseln AND B.BeginTime > E.EndTime - '00:00:10'
Die Genauigkeit der DateDiff
Ich möchte auch darauf hinweisen, dass DateDiff
nicht zurück verstrichene Zeit, sondern zählt die Anzahl der Grenzen gekreuzt. Wenn ein Anruf unter Verwendung von Sekunden DateDiff
1
zurückkehrt, könnte dies 3 ms
verstrichene Zeit bedeuten, oder es könnte 1997 ms
bedeuten! Dies ist im Wesentlichen eine Genauigkeit von + - 1 Zeiteinheit. Für die bessere Genauigkeit von + - 1/2 Zeiteinheit, würden Sie die folgende Abfrage zu vergleichen 0
wollen EndTime - BegTime
:
SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'
Dies hat nun einen maximalen Rundungsfehler von nur einer Sekunde insgesamt, nicht von zwei (in der Tat ein Boden () Betrieb). Beachten Sie, dass Sie nur den datetime
Datentyp subtrahieren können - ein date
oder einen time
Wert subtrahieren Sie die bessere Präzision (eine ganze Menge datetime
, DateAdd
zu bekommen und möglicherweise auch andere Junk DateDiff
oder andere Methoden müßten konvertieren zu verwenden, oder vielleicht eine höhere Präzision Zeiteinheit mit und Dividieren).
Dieses Prinzip ist besonders wichtig, wenn größere Einheiten zu zählen, wie Stunden, Tage oder Monate. Ein DateDiff
von 1 month
62 Tage auseinander liegen könnte (man denke 1. Juli 2013 - 31. August 2013)!
über die es "Arbeit", benötigen Sie einen Index verwenden
verwenden, um eine berechnete Spalte mit einem Index oder eine Ansicht mit einem Index, sonst werden Sie Table-Scan. wenn Sie genug Reihen erhalten, fühlen Sie die PAIN der Slow-Scan!
berechnete Spalte & Index:
ALTER TABLE MyTable ADD
ComputedDate AS DATEDIFF(ss,BegTime, EndTime)
GO
CREATE NONCLUSTERED INDEX IX_MyTable_ComputedDate ON MyTable
(
ComputedDate
) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Erstellen Sie eine Ansicht & Index:
CREATE VIEW YourNewView
AS
SELECT
KeyValues
,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
GO
CREATE CLUSTERED INDEX IX_YourNewView
ON YourNewView(InitialSave)
GO
Sie müssen die Funktion anstelle des Spaltenalias verwenden – das Gleiche gilt für count(*) usw.PITA-BROT.
Als Alternative, können Sie berechnete Spalten .