На сьогоднішній день передача є достатнім, але це гарна ідея?


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
Це відмінний елемент Connect. :)
Роб Фарлі

1
Сайт Connect було видалено, а також Sargable у Вікіпедії
Ivanzinho

Відповіді:


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

Я знаю, що в цьому є давній Great Answer® від Martin, але я хотів внести деякі зміни в поведінку тут у новіших версіях SQL Server. Здається, це було протестовано до 2008R2.

З новими підказками USE, які дозволяють зробити деяку оцінку кардинальності подорожі в часі, ми можемо побачити, коли все змінилося.

Використовуючи ті ж налаштування, що і у SQL Fiddle.

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 

Плани всього цього доступні тут . Обидва рівні 100 і 110 дають план пошуку ключів, але, починаючи з рівня 120, ми починаємо отримувати той самий план сканування зі 100 оцінками рядків. Це стосується рівня 140.

Горіхи

Горіхи

Горіхи

Оцінка кардинальності >= '20130101', < '20130102'планів залишається 100, що і очікувалося.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.