Вопрос

Я использую 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(*) и т. д.ПИТА.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top