Используя datediff функция в T-SQL в

Я использую DATEDIFF в инструкции SQL. Я выбираю его,и мне нужно использовать его в предложении WHERE. Это утверждение не работает...

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10

Он выводит сообщение: недопустимое имя столбца "InitialSave"

но это утверждение отлично работает...

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10

программист во мне говорит, что это неэффективно (кажется, я вызываю функцию дважды).

Итак, два вопроса. Почему не первое утверждение работа? Неэффективно ли это делать с помощью второго оператора?

5 ответов


вы не можете получить доступ к столбцам, определенным в инструкции select в инструкции where, потому что они не генерируются до тех пор, пока where не будет выполнено.

вы можете сделать это правда

select InitialSave from 
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10

как sidenote-это по существу перемещает DATEDIFF в оператор where с точки зрения того, где он впервые определен. Использование функций на столбцах в операторах where приводит к тому, что индексы не используются так эффективно и их следует избегать, если это возможно, однако, если вам нужно использовать datediff тогда вы должны это сделать!


Примечание: когда я первоначально написал этот ответ, я сказал, что индекс на одном из столбцов может создать запрос, который работает лучше, чем другие ответы (и упомянул Дэна Фуллера). Однако я не думал на 100% правильно. Дело в том, что без вычисляемого столбца или индексированного (материализованного) представления полное сканирование таблицы будет требуются, потому что два сравниваемых столбца даты из то же самое стол!

я верю в приведенной ниже информации все еще есть ценность, а именно: 1) возможность улучшения производительности в правильной ситуации, как при сравнении столбцов из разных таблиц, и 2) содействие привычке разработчиков SQL следовать лучшей практике и переформулировать свое мышление в правильном направлении.

Создание Условий Sargable

лучшая практика, о которой я говорю, - это перемещение одного столбца, чтобы быть в одиночестве на одной стороне сравнения оператор, вроде так:

SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'

как я уже сказал, это не позволит избежать сканирования на одном столе, однако, в такой ситуации это может иметь огромное значение:

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 находится в обоих условиях теперь только на одной стороне сравнения. Предполагая, что BeginTime таблица имеет гораздо меньше строк, и EndTime таблица имеет индекс по столбцу EndTime, это будет работать намного лучше, чем что-либо с помощью DateDiff(second, B.BeginTime, E.EndTime). Сейчас sargable, что означает существует допустимый "аргумент поиска" --так как engine сканирование the BeginTime таблицы искать на EndTime таблица. Требуется тщательный выбор того, какой столбец находится сам по себе с одной стороны оператора-может быть, стоит поэкспериментировать, поставив BeginTime сам по себе, выполнив некоторую алгебру, чтобы переключиться на AND B.BeginTime > E.EndTime - '00:00:10'

точность DateDiff

я должен также отметить, что DateDiff не возвращает истек времени, но вместо этого подсчитывает количество границы пересек. Если вызов DateDiff через несколько секунд возвращает 1, это значит 3 ms прошедшее время, или это может означать 1997 ms! Это по существу точность единиц времени + - 1. Для лучшей точности единицы времени + - 1/2 вам понадобится следующий запрос comparing 0 to EndTime - BegTime:

SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'

теперь максимальная ошибка округления составляет всего одну секунду, а не две (в влияние, деятельность Пола ()). Обратите внимание, что вы можете вычесть только datetime тип данных--вычесть date или time значение, которое вам нужно будет преобразовать в datetime или использовать другие методы, чтобы получить лучшую точность (много DateAdd, DateDiff и, возможно, другой мусор,или, возможно, с использованием более высокой точности единицы времени и деления).

этот принцип особенно важен при подсчете больших единиц, таких как часы, дни или месяцы. А DateDiff of 1 month может быть 62 дней (думаю 1 июля 2013 - 31 августа 2013)!


помимо того, что он "работает", вам нужно использовать индекс

используйте вычисляемый столбец с индексом или представление с индексом, иначе вы будете сканировать таблицу. когда вы получите достаточно строк, вы почувствуете боль медленного сканирования!

вычисляемый столбец и индекс:

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

создать представление и индекс:

CREATE VIEW YourNewView
AS
SELECT
    KeyValues
        ,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
    FROM MyTable
GO
CREATE CLUSTERED INDEX IX_YourNewView
    ON YourNewView(InitialSave)
GO

вы должны использовать функцию вместо псевдонима столбца - это то же самое с count (*) и т. д. ЛАВАШ.


в качестве альтернативы вы можете использовать вычисляемые столбцы.