Domanda
Sto utilizzando DATEDIFF in un'istruzione SQL.Lo sto selezionando e devo usarlo anche nella clausola WHERE.Questa affermazione non funziona...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10
Dà il messaggio: Nome colonna non valido "InitialSave"
Ma questa affermazione funziona bene...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10
Il programmatore che è in me dice che questo è inefficiente (sembra che io stia chiamando la funzione due volte).
Quindi due domande.Perché la prima affermazione non funziona?È inefficiente farlo utilizzando la seconda istruzione?
Soluzione
Non è possibile accedere colonne definite nella dichiarazione prescelta nella dichiarazione in cui, perché non sono generati fino a dopo la quale ha eseguito.
Si può fare questo però
select InitialSave from
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10
Come sidenote - questo si muove essenzialmente la DATEDIFF in cui economico, in termini di dove è prima definito. Uso delle funzioni su colonne in cui le dichiarazioni cause indici non da utilizzare nel modo più efficiente e devono essere evitati, se possibile, se hai avuto modo di utilizzare datediff allora devi farlo!
Altri suggerimenti
Nota: Quando originariamente ho scritto questa risposta, ho detto che un indice su una delle colonne potrebbe creare una query con prestazioni migliori rispetto ad altre risposte (e ho menzionato quella di Dan Fuller).Tuttavia, non pensavo correttamente al 100%.Il fatto è che, senza una colonna calcolata o una vista indicizzata (materializzata), verrà eseguita una scansione completa della tabella necessario, perché le due colonne di date confrontate provengono da Stesso tavolo!
Credo che ci sia ancora valore nelle informazioni seguenti, vale a dire 1) la possibilità di migliorare le prestazioni nella situazione giusta, come quando il confronto avviene tra colonne di tabelle diverse, e 2) promuovere l'abitudine negli sviluppatori SQL di seguire le migliori pratiche e rimodellare il loro pensiero nella giusta direzione.
Rendere le condizioni Sargable
La migliore pratica a cui mi riferisco è quella di spostare una colonna in modo che sia sola su un lato dell'operatore di confronto, in questo modo:
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'
Come ho detto, questo non eviterà la scansione su una singola tabella, tuttavia, in una situazione come questa potrebbe fare un'enorme differenza:
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
EndTime
in entrambe le condizioni è ora solo da una parte del confronto.Supponendo che il BeginTime
la tabella ha molte meno righe e il file EndTime
la tabella ha un indice sulla colonna EndTime
, questo funzionerà molto, molto meglio di qualsiasi altra cosa DateDiff(second, B.BeginTime, E.EndTime)
.È adesso sargable, il che significa che esiste un "argomento di ricerca" valido, così come il motore scansioni IL BeginTime
tavolo, può cercare dentro EndTime
tavolo.È necessaria un'attenta selezione di quale colonna si trova da sola su un lato dell'operatore: può valere la pena sperimentare inserendo BeginTime
da solo eseguendo un po' di algebra a cui passare AND B.BeginTime > E.EndTime - '00:00:10'
Precisione di DateDiff
Dovrei anche sottolinearlo DateDiff
non ritorna trascorso tempo, ma conta invece il numero di confini attraversato.Se una chiamata a DateDiff
utilizzando i ritorni dei secondi 1
, questo potrebbe significare 3 ms
tempo trascorso, o potrebbe significare 1997 ms
!Questa è essenzialmente una precisione di +- 1 unità di tempo.Per una migliore precisione di +- 1/2 unità di tempo, ti consigliamo di confrontare la seguente query 0
A EndTime - BegTime
:
SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'
Questo ora ha un errore di arrotondamento massimo di solo un secondo totale, non due (in effetti, un'operazione floor()).Tieni presente che puoi solo sottrarre il datetime
tipo di dati: per sottrarre a date
o a time
valore in cui dovresti convertire datetime
o utilizzare altri metodi per ottenere la massima precisione (molti DateAdd
, DateDiff
e possibilmente altra spazzatura, o forse utilizzando un'unità di tempo e una divisione di precisione più elevata).
Questo principio è particolarmente importante quando si contano unità più grandi come ore, giorni o mesi.UN DateDiff
Di 1 month
potrebbero essere a 62 giorni di distanza (si pensi al 1 luglio 2013 - 31 agosto 2013)!
al di là che lo rende "lavoro", è necessario utilizzare un indice
utilizzare una colonna calcolata con un indice o una vista con un indice, altrimenti si scansione di tabella. quando si ottiene abbastanza righe, vi sentirete la PAIN della scansione lenta!
colonna calcolata e 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
creare un indice di vista &:
CREATE VIEW YourNewView
AS
SELECT
KeyValues
,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
GO
CREATE CLUSTERED INDEX IX_YourNewView
ON YourNewView(InitialSave)
GO
Si deve utilizzare la funzione al posto del alias di colonna - è lo stesso con count (*), ecc PITA
.Come alternativa, è possibile utilizzare colonne calcolate .