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?

È stato utile?

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 .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top