Я замечаю, что когда происходят события разлива в базу данных tempdb (вызывающие медленные запросы), часто оценки строк оказываются не подходящими для конкретного соединения. Я видел события разлива с объединениями и хэш-соединениями, и они часто увеличивают время выполнения в 3 раза до 10 раз. Этот вопрос касается того, как улучшить оценку строк при условии, что это уменьшит вероятность разливов.
Фактическое количество рядов 40к.
Для этого запроса план показывает неверную оценку строки (11,3 строки):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Для этого запроса план показывает хорошую оценку строк (56 тыс. Строк):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
Можно ли добавить статистику или подсказки для улучшения оценок строк в первом случае? Я попытался добавить статистику с определенными значениями фильтра (свойство = 2840), но либо не смог получить правильную комбинацию, либо, возможно, он игнорируется, потому что ObjectId неизвестен во время компиляции и может выбирать среднее значение для всех ObjectIds.
Есть ли какой-нибудь режим, в котором он сначала выполняет тестовый запрос, а затем использует его для определения оценок строк, или он должен летать вслепую?
Это конкретное свойство имеет много значений (40 КБ) для нескольких объектов и ноль для подавляющего большинства. Я был бы счастлив с подсказкой, где можно указать максимальное ожидаемое количество строк для данного объединения. Это обычно преследующая проблема, потому что некоторые параметры могут быть определены динамически как часть объединения или будут лучше размещены в представлении (без поддержки переменных).
Есть ли какие-либо параметры, которые можно настроить, чтобы минимизировать вероятность разлива в базу данных (например, минимальное количество памяти на запрос)? Надежный план не оказал влияния на оценку.
Изменить 2013.11.06 : Ответ на комментарии и дополнительную информацию:
Вот изображения плана запроса. Предупреждения о предикате кардинальности / поиска с помощью convert ():
Согласно комментарию @ Аарона Бертранда, я попытался заменить convert () в качестве теста:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
Как странный, но успешный интерес, он также позволил замкнуть поиск:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Оба из них перечисляют правильный поиск ключа, но только первые перечисляют «Выход» ObjectId. Я думаю, это указывает на то, что второе действительно короткое замыкание?
Может ли кто-нибудь проверить, выполняются ли когда-нибудь однорядные исследования, чтобы помочь с оценками строк? Кажется неправильным ограничивать оптимизацию только оценками гистограммы, когда однострочный поиск PK может значительно повысить точность поиска в гистограмме (особенно, если есть вероятность разлива или история). Когда в реальном запросе есть 10 таких вложенных соединений, в идеале они должны выполняться параллельно.
Дополнительное замечание: поскольку sql_variant хранит свой базовый тип (SQL_VARIANT_PROPERTY = BaseType) в самом поле, я ожидаю, что convert () будет почти бесплатным, если он «непосредственно» конвертируем (например, не строка в десятичную, а скорее int в int или возможно int to bigint). Так как это не известно во время компиляции, но может быть известно пользователю, возможно, функция «AssumeType (type, ...)» для sql_variants позволила бы обрабатывать их более прозрачно.
declare @a bigint =
что вы сделали, кажется мне естественным решением, почему это недопустимо?
CONVERT()
в столбцах, а затем присоединиться к ним. Это, конечно, не эффективно в большинстве случаев. В этом конкретном случае необходимо преобразовать только одно значение, так что, вероятно, это не проблема, но какие индексы у вас в таблице? Проекты EAV обычно работают хорошо, только при правильной индексации (что означает много индексов в обычно узких таблицах).