Я намагаюся отримати ще одну ефективність із запиту, який отримує доступ до таблиці з ~ 250 мільйонами записів. З мого читання фактичного (не оціненого) плану виконання, перше вузьке місце - це запит, який виглядає приблизно так:
select
b.stuff,
a.added,
a.value
from
dbo.hugetable a
inner join
#smalltable b on a.fk = b.pk
where
a.added between @start and @end;
Далі див. Нижче визначення визначних таблиць та індексів.
План виконання вказує, що вкладений цикл використовується на #smalltable, і що сканування індексу над величезним таблицею виконується 480 разів (для кожного рядка в #smalltable). Це здається мені назад, тому я намагався змусити замість цього об'єднати об'єднання:
select
b.stuff,
a.added,
a.value
from
dbo.hugetable a with(index = ix_hugetable)
inner merge join
#smalltable b with(index(1)) on a.fk = b.pk
where
a.added between @start and @end;
Індекс, про який йдеться (див. Нижче для повного визначення), охоплює стовпці fk (предикат приєднання), доданий (використовується у пункті де) та id (непотрібний) у порядку зростання та включає значення .
Однак, коли я це роблю, запит закінчується від 2 1/2 хвилини до більше 9. Я би сподівався, що підказки змусять більш ефективно приєднатись, що робить лише один прохід над кожною таблицею, але явно ні.
Будь-які вказівки вітаються. За необхідності надається додаткова інформація.
Оновлення (2011/06/02)
Реорганізувавши індексацію в таблиці, я досяг значних результатів, проте я зіткнувся з новою перешкодою, коли справа стосується узагальнення даних у величезній таблиці. Результатом є підсумок за місяцями, який наразі виглядає наступним чином:
select
b.stuff,
datediff(month, 0, a.added),
count(a.value),
sum(case when a.value > 0 else 1 end) -- this triples the running time!
from
dbo.hugetable a
inner join
#smalltable b on a.fk = b.pk
group by
b.stuff,
datediff(month, 0, a.added);
В даний час величезний таблиця має кластерний індекс pk_hugetable (added, fk)
(первинний ключ), а некластеризований індекс - іншим шляхом ix_hugetable (fk, added)
.
Без четвертого стовпця вище оптимізатор використовує вкладене вкладене цикл, як і раніше, використовуючи #smalltable як зовнішній вхід, а некластеризований індекс шукають як внутрішній цикл (виконуючи ще раз 480 разів). Що мене хвилює - це невідповідність між орієнтовними рядками (12 958,4) та фактичними рядками (74 668 468). Відносна вартість цих прагнень становить 45%. Однак час роботи триває менше хвилини.
За допомогою 4-го стовпця час роботи збільшується до 4 хвилин. Цього разу він шукає на кластерному індексі (2 виконання) за однакову відносну вартість (45%), агрегує через хеш-матч (30%), потім робить хеш-з'єднання на #smalltable (0%).
Я не впевнений, що стосується мого наступного курсу дій. Мене хвилює те, що не гарантується ні пошук діапазону дат, ні присудок приєднання, або навіть все, що може різко зменшити набір результатів. Діапазон дат у більшості випадків обріже лише 10-15% записів, а внутрішнє з'єднання на fk може відфільтрувати, можливо, 20-30%.
Як вимагає Уілл А, результати sp_spaceused
:
name | rows | reserved | data | index_size | unused
hugetable | 261774373 | 93552920 KB | 18373816 KB | 75167432 KB | 11672 KB
#smalltable визначається як:
create table #endpoints (
pk uniqueidentifier primary key clustered,
stuff varchar(6) null
);
Тоді як dbo.hugetable визначається як:
create table dbo.hugetable (
id uniqueidentifier not null,
fk uniqueidentifier not null,
added datetime not null,
value decimal(13, 3) not null,
constraint pk_hugetable primary key clustered (
fk asc,
added asc,
id asc
)
with (
pad_index = off, statistics_norecompute = off,
ignore_dup_key = off, allow_row_locks = on,
allow_page_locks = on
)
on [primary]
)
on [primary];
З таким показником визначено:
create nonclustered index ix_hugetable on dbo.hugetable (
fk asc, added asc, id asc
) include(value) with (
pad_index = off, statistics_norecompute = off,
sort_in_tempdb = off, ignore_dup_key = off,
drop_existing = off, online = off,
allow_row_locks = on, allow_page_locks = on
)
on [primary];
Поле id є зайвим - артефакт попередньої DBA, який наполягав на тому, що всі таблиці скрізь повинні мати GUID, не виняток.