Каждая опция, которая включает в себя манипуляции CAST или TRUNCATE или DATEPART в поле datetime, имеет одну и ту же проблему: запрос должен сканировать весь набор результатов (40 КБ), чтобы найти различные даты. Производительность может незначительно отличаться в зависимости от реализации.
Что вам действительно нужно, так это иметь индекс, который может мгновенно генерировать ответ. Вы можете иметь постоянный вычисляемый столбец с индексом, который (требует изменения структуры таблицы), или индексированное представление ( требует Enterprise Edition для QO, чтобы рассмотреть индекс «из коробки»).
Сохраняемый вычисляемый столбец:
alter table foo add date_only as convert(char(8), [datetimecolumn], 112) persisted;
create index idx_foo_date_only on foo(date_only);
Индексированное представление:
create view v_foo_with_date_only
with schemabinding as
select id
, convert(char(8), [datetimecolumn], 112) as date_only
from dbo.foo;
create unique clustered index idx_v_foo on v_foo_with_date_only(date_only, id);
Обновление
Чтобы полностью исключить сканирование, можно использовать индексированное представление с условным обозначением GROUP BY, например:
create view v_foo_with_date_only
with schemabinding as
select
convert(char(8), [d], 112) as date_only
, count_big(*) as [dummy]
from dbo.foo
group by convert(char(8), [d], 112)
create unique clustered index idx_v_foo on v_foo_with_date_only(date_only)
Запрос select distinct date_only from foo
будет использовать это индексированное представление вместо этого. Технически все еще сканирование, но по уже «отдельному» индексу, поэтому сканируются только необходимые записи. Я считаю, что это хак, я бы не рекомендовал это для живого производственного кода.
AFAIK SQL Server не имеет возможности сканирования истинного индекса с пропуском повторов, т.е. ищите вершину, затем ищите больше, чем вершина, затем последовательно ищите больше, чем последний найденный.