В ролях на сегодняшний день можно сказать, что это хорошая идея?


47

В SQL Server 2008 был добавлен тип данных даты .

Кастинг datetimeстолбца dateявляется sargable и может использовать индекс на datetimeколонке.

select *
from T
where cast(DateTimeCol as date) = '20130101';

Другой вариант - использовать диапазон.

select *
from T
where DateTimeCol >= '20130101' and
      DateTimeCol < '20130102'

Являются ли эти запросы одинаково хорошими или один предпочтительнее другого?


4
Что говорит план выполнения?
a_horse_with_no_name

3
Я не могу не заметить, что LINQ2SQL генерирует SQL, where cast(date_column as date) = 'value'когда представлен с C # аналогично where obj.date_column.Date == date_variable.
GSerg

6
Это отличный пункт подключения. :)
Роб Фарли

1
Сайт Connect был удален, а также Sargable в Википедии
Иванзиньо,

Ответы:


59

Механизм, обеспечивающий гибкость кастинга на сегодняшний день, называется динамическим поиском .

SQL Server вызывает внутреннюю функцию, GetRangeThroughConvertчтобы получить начало и конец диапазона.

Несколько удивительно, что это не тот же диапазон, что и ваши буквальные значения.

Создание таблицы со строкой на страницу и 1440 строк в день

CREATE TABLE T
  (
     DateTimeCol DATETIME PRIMARY KEY,
     Filler      CHAR(8000) DEFAULT 'X'
  );

WITH Nums(Num)
     AS (SELECT number
         FROM   spt_values
         WHERE  type = 'P'
                AND number BETWEEN 1 AND 1440),
     Dates(Date)
     AS (SELECT {d '2012-12-30'} UNION ALL
         SELECT {d '2012-12-31'} UNION ALL
         SELECT {d '2013-01-01'} UNION ALL
         SELECT {d '2013-01-02'} UNION ALL
         SELECT {d '2013-01-03'})
INSERT INTO T
            (DateTimeCol)
SELECT DISTINCT DATEADD(MINUTE, Num, Date)
FROM   Nums,
       Dates 

Потом работает

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT *
FROM   T
WHERE  DateTimeCol >= '20130101'
       AND DateTimeCol < '20130102'

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'; 

Первый запрос имеет 1443чтение, а второй 2883- чтение всего дополнительного дня, а затем отбрасывает его против остаточного предиката.

План показывает, что предикат поиска

Seek Keys[1]: Start: DateTimeCol > Scalar Operator([Expr1006]), 
               End: DateTimeCol < Scalar Operator([Expr1007])

Таким образом, вместо >= '20130101' ... < '20130102'этого читает, а > '20121231' ... < '20130102'затем отбрасывает все 2012-12-31строки.

Другой недостаток - полагаться на то, что оценки мощности могут быть не такими точными, как при традиционном запросе диапазона. Это можно увидеть в исправленной версии вашего SQL Fiddle .

Все 100 строк в таблице теперь соответствуют предикату (с разницей в 1 минуту все в один и тот же день).

Второй (диапазонный) запрос правильно оценивает, что 100 будет соответствовать, и использует сканирование кластерного индекса. CAST( AS DATE)Запрос неправильно оценивает , что только одна строки будет соответствовать и производит план с ключевым поиском.

Статистика не полностью игнорируется. Если все строки в таблице одинаковы datetimeи соответствуют предикату (например, 20130101 00:00:00или 20130101 01:00:00), то план показывает сканирование кластерного индекса с оценочными 31,6228 строками.

100 ^ 0.75 = 31.6228

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

Если все строки в таблице имеют одинаковые значения datetimeи не соответствуют предикату (например 20130102 01:00:00), тогда он возвращается к оценочному количеству строк, равному 1, и к плану с поисками.

Для случаев, когда таблица имеет более одного DISTINCTзначения, предполагаемые строки выглядят такими же, как если бы запрос выполнялся точно 20130101 00:00:00.

Если статистическая гистограмма имеет определенный шаг, 2013-01-01 00:00:00.000тогда оценка будет основана на EQ_ROWS(то есть, не принимая во внимание другое время на эту дату). В противном случае, если нет шага, это выглядит так, как будто он использует AVG_RANGE_ROWSокружающие шаги.

Так datetimeкак во многих системах точность составляет около 3 мс, фактических повторяющихся значений будет очень мало, и это число будет равно 1.


1
Привет, Мартин, не могли бы вы добавить TL;DRдеталь с несколькими пунктами в разных случаях, добавив, будет ли в этом случае приведение актеров на сегодняшний день - это хорошая идея или нет?
ТТ.

6
@TT. Я думаю, дело в том, что это не очень хорошая идея. Почему вы хотите использовать метод, который требует шпаргалку?
Аарон Бертран

10

Я знаю, что у этого есть давний Великий Ответ® от Мартина, но я хотел добавить некоторые изменения в поведение здесь в более новых версиях SQL Server. Похоже, это было проверено только до 2008R2.

С новыми подсказками USE, которые делают возможным некоторое путешествие во времени для оценки количества элементов , мы можем видеть, когда что-то изменилось.

Используя ту же настройку, что и в скрипте SQL.

CREATE TABLE T ( ID INT IDENTITY PRIMARY KEY, DateTimeCol DATETIME, Filler CHAR(8000) NULL );

CREATE INDEX IX_T_DateTimeCol ON T ( DateTimeCol );


WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
     E02(N) AS (SELECT 1 FROM E00 a, E00 b),
     E04(N) AS (SELECT 1 FROM E02 a, E02 b),
     E08(N) AS (SELECT 1 FROM E04 a, E04 b),
     Num(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY E08.N) FROM E08)
INSERT INTO T(DateTimeCol)
SELECT TOP 100 DATEADD(MINUTE, Num.N, '20130101')
FROM Num;

Мы можем протестировать разные уровни следующим образом:

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_100' ));
GO

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_110' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_120' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_130' ));
GO 

SELECT *
FROM   T
WHERE  CAST(DateTimeCol AS DATE) = '20130101'
OPTION ( USE HINT ( 'QUERY_OPTIMIZER_COMPATIBILITY_LEVEL_140' ));
GO 

Планы на все это доступны здесь . Уровни Compat 100 и 110 дают план поиска ключа, но начиная с уровня Compat 120, мы начинаем получать один и тот же план сканирования с оценками в 100 строк. Это верно до уровня 140 соотечественника.

NUTS

NUTS

NUTS

Оценка количества кардинальных >= '20130101', < '20130102'планов остается на уровне 100, что и ожидалось.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.