Использование 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
Программист во мне говорит, что это неэффективно (кажется, я дважды вызываю функцию).
Итак, два вопроса.Почему первое утверждение не работает?Неэффективно ли делать это, используя второй оператор?
Решение
Вы не можете получить доступ к столбцам, определенным в операторе select в оператореwhere, поскольку они не генерируются до тех пор, пока не будет выполнен операторwhere.
Однако вы можете сделать это
select InitialSave from
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10
В качестве примечания: это по существу перемещает DATEDIFF в операторwhere с точки зрения того, где он впервые определен.Использование функций для столбцов в операторахwhere приводит к тому, что индексы используются не так эффективно, и его следует избегать, если это возможно, однако, если вам нужно использовать dateiff, вы должны это сделать!
Другие советы
Примечание: Когда я изначально писал этот ответ, я сказал, что индекс по одному из столбцов может создать запрос, который будет работать лучше, чем другие ответы (и упомянул Дэна Фуллера).Однако я думал не на 100% правильно.Дело в том, что без вычисляемого столбца или индексированного (материализованного) представления полное сканирование таблицы будет необходимый, поскольку два сравниваемых столбца дат взяты из такой же стол!
Я считаю, что приведенная ниже информация по-прежнему имеет ценность, а именно: 1) возможность повышения производительности в правильной ситуации, например, при сравнении столбцов из разных таблиц, и 2) развитие у разработчиков SQL привычки следовать передовому опыту и изменять форму. их мышление в правильном направлении.
Сделать условия доступными
Лучшая практика, о которой я говорю, — это переместить один столбец так, чтобы он оставался один по одну сторону от оператора сравнения, например:
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)
.Сейчас разборчивый, что означает, что существует действительный «аргумент поиска» — так как движок сканирует тот BeginTime
стол, он может искать в EndTime
стол.Требуется тщательный выбор того, какой столбец находится отдельно по одну сторону от оператора — возможно, стоит поэкспериментировать, поставив BeginTime
сам по себе, проделав некоторую алгебру, чтобы переключиться на AND B.BeginTime > E.EndTime - '00:00:10'
Точность DateDiff
Я также должен отметить, что DateDiff
не возвращается истек время, а вместо этого подсчитывает количество границы пересек.Если звонок в DateDiff
использование секунд возвращает 1
, это может означать 3 ms
прошедшее время, или это может означать 1997 ms
!По сути, это точность +- 1 единица времени.Для большей точности +- 1/2 единицы времени вам понадобится следующий запрос, сравнивающий 0
к EndTime - BegTime
:
SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'
Теперь максимальная ошибка округления составляет всего одну секунду, а не две (по сути, операция Floor()).Обратите внимание, что вы можете вычесть только datetime
тип данных - для вычитания date
или time
значение, которое вам придется преобразовать в datetime
или используйте другие методы для получения большей точности (много DateAdd
, DateDiff
и, возможно, другой мусор или, возможно, использование более точной единицы измерения времени и деления).
Этот принцип особенно важен при подсчете более крупных единиц, таких как часы, дни или месяцы.А DateDiff
из 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(*) и т. д.ПИТА.
В качестве альтернативы вы можете использовать вычисляемые столбцы.