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?

Foi útil?

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 .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top