Pergunta
Eu estou usando DATEDIFF em uma instrução SQL. Estou selecionando-o, e eu preciso usá-lo na cláusula WHERE também. Esta declaração não funciona ...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10
Ela dá a mensagem: nome de coluna inválido "InitialSave"
Mas esta declaração funciona bem ...
SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10
O programador em mim diz que este é ineficiente (parece que eu estou chamando a função duas vezes).
Assim, duas perguntas. Por que não faz o primeiro trabalho afirmação? É ineficiente para fazê-lo usando a segunda instrução?
Solução
Você não pode colunas de acesso definidos na instrução SELECT na instrução WHERE, porque não são gerados até depois do que foi executado.
Você pode fazer isso no entanto
select InitialSave from
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10
Como nota - este, essencialmente move o DATEDIFF no qual declaração em termos de onde ele é definido pela primeira vez. Usando funções em colunas onde as declarações faz com que índices para não ser utilizado da forma mais eficiente e deve ser evitado, se possível, no entanto, se você tem que usar datediff então você tem que fazê-lo!
Outras dicas
Nota: Quando eu originalmente escreveu esta resposta eu disse que um índice em uma das colunas poderia criar uma consulta que executa melhor do que outras respostas (e mencionou Dan Fuller). No entanto, eu não estava pensando 100% corretamente. O fato é que, sem uma coluna computada ou indexados (materializado) vista, uma varredura completa da tabela vai ser necessário , porque as duas colunas de data a ser comparados são do mesma mesa!
Eu acredito que ainda há valor nas informações a seguir, nomeadamente 1) a possibilidade de um melhor desempenho na situação certa, como quando a comparação é entre as colunas de tabelas diferentes, e 2) promover o hábito de desenvolvedores de SQL de seguir melhor prática e remodelar o seu pensamento na direção certa.
Fazer Condições sargable
A melhor prática eu estou me referindo é um dos mover uma coluna para ficar sozinho em um lado do operador de comparação, assim:
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'
Como eu disse, isso não vai evitar uma varredura em uma única tabela, no entanto, em uma situação como esta poderia fazer uma enorme diferença:
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
é em ambas as condições agora só de um lado da comparação. Assumindo que a tabela de BeginTime
tem muitos menos linhas, e a tabela de EndTime
tem um índice em EndTime
coluna, este irá realizar muito, muito melhor do que qualquer coisa usando DateDiff(second, B.BeginTime, E.EndTime)
. Agora é sargable , o que significa que há um válido "argumento de pesquisa" - assim como o motor scans tabela de BeginTime
, pode procurar na tabela de EndTime
. A seleção cuidadosa de qual coluna é por si só é necessária em um lado do operador - pode valer a pena experimentar, colocando BeginTime
por si só fazendo alguma álgebra para mudar para AND B.BeginTime > E.EndTime - '00:00:10'
Precision de DateDiff
Gostaria também de salientar que DateDiff
não retornar decorrido tempo, mas em vez disso conta o número de limites cruzados. Se uma chamada para DateDiff
usando segundos retornos 1
, isso pode significar tempo 3 ms
decorrido, ou pode significar 1997 ms
! Esta é essencialmente uma precisão de + - 1 unidades de tempo. Para melhor precisão de + - 1/2 unidade de tempo, você iria querer a seguinte consulta comparando 0
para EndTime - BegTime
:
SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'
Esta agora tem um erro máximo de arredondamento de apenas um segundo total, e não dois (na verdade, um andar () operação). Note que você só pode subtrair o tipo de dados datetime
- para subtrair um date
ou um valor time
você teria que converter para datetime
ou usar outros métodos para obter a melhor precisão (um monte de DateAdd
, DateDiff
e possivelmente outras porcarias, ou talvez usando uma unidade de tempo maior precisão e dividindo).
Este princípio é especialmente importante quando a contagem de unidades maiores, como horas, dias ou meses. A DateDiff
de 1 month
poderia ser 62 dias de intervalo (pense primeiro de julho de 2013 - 31 de agosto de 2013)!
além tornando-o "trabalho", você precisa usar um índice
usar uma coluna computada com um índice ou uma exibição com um índice, caso contrário você vai varredura da tabela. quando você começa linhas suficientes, você vai sentir a DOR da verificação lento!
computado coluna e índice:
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
criar uma visão e índice:
CREATE VIEW YourNewView
AS
SELECT
KeyValues
,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
GO
CREATE CLUSTERED INDEX IX_YourNewView
ON YourNewView(InitialSave)
GO
Você tem que usar a função em vez do alias de coluna - é o mesmo com count (*), etc. PITA
.Como alternativa, você pode usar colunas computadas .