На сервере с 32 ГБ мы используем SQL Server 2014 SP2 с максимальной памятью 25 ГБ, у нас есть две таблицы, здесь вы найдете упрощенную структуру обеих таблиц:
CREATE TABLE [dbo].[Settings](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceId] [int] NULL,
[typeID] [int] NULL,
[remark] [varchar](max) NULL,
CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Resources](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceUID] [int] NULL,
CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
со следующими некластеризованными индексами:
CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
[resourceUID] ASC
)
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
[resourceId] ASC,
[typeID] ASC
)
База данных настроена на compatibility level
120.
Когда я запускаю этот запрос, есть разливы tempdb
. Вот как я выполняю запрос:
exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Если не выбрать [remark]
поле, разливов не происходит. Моей первой реакцией было то, что разливы произошли из-за небольшого числа предполагаемых строк в операторе вложенного цикла.
Поэтому я добавляю 5 столбцов даты и времени и 5 целочисленных столбцов в таблицу настроек и добавляю их в свой оператор выбора. Когда я выполняю запрос, никаких разливов не происходит.
Почему разливы происходят только тогда, когда [remark]
выбрано? Это, вероятно, как-то связано с тем, что это varchar(max)
. Что я могу сделать, чтобы избежать проливания tempdb
?
Добавление OPTION (RECOMPILE)
к запросу не имеет значения.
источник
select r.id, LEFT(remark, 512)
(или любой разумной длины подстроки может быть).Ответы:
Здесь будет несколько возможных обходных путей.
Вы можете вручную настроить выделение памяти, хотя я бы, вероятно, не пошел по этому пути.
Вы также можете использовать CTE и TOP, чтобы переместить сортировку ниже, прежде чем захватывать столбец максимальной длины. Это будет выглядеть примерно так:
Подтверждение концепции dbfiddle здесь . Пример данных все равно будет оценен!
Если вы хотите прочитать отличный анализ Пола Уайта, читайте здесь.
источник
Разлив происходит, когда вы включаете этот столбец, потому что у вас недостаточно большой объем памяти для сортировки больших строковых данных.
Вы не получаете достаточно большой доступ к памяти, потому что фактическое количество строк в 10 раз больше, чем предполагаемое количество строк (1302 фактических против 126 предполагаемых).
Почему оценка выключена? Почему SQL Server считает, что в dbo есть только одна строка с
resourceid
38?Это может быть проблема статистики, которую вы можете проверить, запустив
DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')
и посмотрев счетчик для этого шага гистограммы. Но план выполнения, кажется, указывает на то, что статистика является настолько полной и актуальной, насколько это возможно.Поскольку статистика не помогает, ваша лучшая ставка - это, вероятно, переписывание запроса, о котором Форрест рассказал в своем ответе.
источник
Мне кажется, что
where
предложение в запросе дает проблему, и является причиной низких оценок, даже еслиOPTION(RECOMPILE)
используется.Я создал несколько тестовых данных и в конце концов предложил два решения, сохраняя
ID
полеresources
в переменной (если оно всегда уникально) или во временной таблице, если у нас может быть больше одногоID
.База тестовых записей
Вставьте значения «Поиск», чтобы получить тот же приблизительный набор результатов, что и OP (1300 записей)
Изменить статистику для сравнения и обновления, чтобы соответствовать OP
Оригинальный запрос
Мои оценки еще хуже , с одной оценочной строкой, а 1300 возвращаются. И, как указано в ОП, не имеет значения, если я добавлю
OPTION(RECOMPILE)
Важно отметить, что когда мы избавляемся от предложения where, оценки оказываются на 100% правильными, что ожидается, так как мы используем все данные в обеих таблицах.
Я заставил индексы просто убедиться, что мы используем те же, что и в предыдущем запросе, чтобы доказать
Как и следовало ожидать, хорошие оценки.
Итак, что мы можем изменить, чтобы получить более точные оценки, но при этом стремиться к нашим ценностям?
Если @UID уникален, как в примере, приведенном OP, мы можем поместить сингл
id
, возвращенный из,resources
в переменную, а затем выполнить поиск по этой переменной с помощью OPTION (RECOMPILE)Что дает 100% точные оценки
Но что если в ресурсах есть несколько resourceUID?
добавить некоторые тестовые данные
Это может быть решено с помощью временной таблицы
Опять с точными оценками .
Это было сделано с моим собственным набором данных, YMMV.
Написано с помощью sp_executesql
С переменной
С временной таблицей
Все еще 100% правильные оценки на моем тесте
источник