Я замечаю, что когда происходят события разлива в базу данных 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 обычно работают хорошо, только при правильной индексации (что означает много индексов в обычно узких таблицах).Ответы:
Я не буду комментировать разливы, tempdb или подсказки, потому что запрос кажется довольно простым и требует особого внимания. Я думаю, что оптимизатор SQL-сервера хорошо справится со своей задачей, если для запроса найдутся индексы.
И ваше разбиение на два запроса хорошо, поскольку оно показывает, какие индексы будут полезны. Первая часть:
нужен индекс на
(PropertyId, ObjectId, Sequence)
включениеValue
. Я бы сделал это,UNIQUE
чтобы быть в безопасности. Запрос все равно выдаст ошибку во время выполнения, если будет возвращено более одной строки, поэтому хорошо заранее убедиться, что этого не произойдет, с уникальным индексом:Вторая часть запроса:
нужен индекс на
(PropertyId, ObjectId)
включениеValue
:Если эффективность не улучшится, или эти индексы не использовались, или все еще появляются различия в оценках строк, то необходимо будет дополнительно изучить этот запрос.
В этом случае вероятной причиной является преобразование (необходимое из схемы EAV и хранение разных типов данных в одних столбцах), и ваше решение разбить (как @AAron Bertrand и @Paul White) запрос на две части кажется естественным и путь. Редизайн, чтобы иметь разные типы данных в соответствующих столбцах, может быть другим.
источник
Как частичный ответ на явный вопрос по улучшению статистики ...
Обратите внимание, что оценки строк даже для случая с разбивкой по-отдельности по-прежнему отклоняются в 10 раз (4 К против ожидаемых 40 К).
Гистограмма статистики, вероятно, была слишком тонкой для этого свойства, потому что это длинная (вертикальная) таблица из 3,5 млн. Строк, а это конкретное свойство крайне редкое.
Создайте дополнительную статистику (несколько избыточную со статистикой IX) для разреженного свойства:
Оригиналы:
С удаленным convert () (правильно):
С удаленным convert () (короткое замыкание):
Тем не менее, в ~ 2 раза вероятно, потому что> 99,9% объектов вообще не имеют свойства 2840, определенного на них. Фактически, только для этого тестового случая свойство существует только на 1 из 200 000 различных объектов таблицы строк размером 3,5 МБ. Удивительно, но так близко. Настраивая фильтр на меньшее количество ObjectIds,
Хм, без изменений ... Подчеркнул, что добавлено «с полной проверкой» в конце статистики (может быть, поэтому предыдущие два не сработали) и да:
Ура. Таким образом, в сильно вертикальной таблице с широким охватом IX добавление дополнительной отфильтрованной статистики представляется большим улучшением (особенно для разреженных, но сильно изменяющихся комбинаций клавиш).
источник