Медленная подкачка с помощью SQL-запроса (хранимой процедуры) в SQL Server - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть следующий оператор SQL, который генерируется в динамической хранимой процедуре на основе различных параметров:

SELECT [MetadataId], [DocumentType], InvoiceNumber FROM 
( 
  SELECT [MetadataId], [DocumentType], InvoiceNumber, ROW_NUMBER() 
  OVER (ORDER BY [MetadataId] Asc) 
  AS [Row_ID] 
  FROM [Metadata] 
  WHERE ([DocumentType] = 'Invoice')
) Wrapper 
WHERE Row_ID BETWEEN 999980 AND 1000000

, где Row_ID изменяется в зависимости от текущей страницы моей сетки.

Приведенный выше запрос прекрасно работает, когда я сначала перехожу со страницы 1 на страницу 2,3,4,5 и т. Д., Но я не могу сказать то же самое, если сразу перехожу со страницы 1 на страницу 50 000, то есть последняя страница в моей тестовой базе данных, которая содержит 1 миллион случайно сгенерированных случайным образом счетов с размером моей страницы 20.

Загрузка занимает около 29/30 секунд, а объем оперативной памяти, используемой моим экземпляром SQL Server, увеличивается с 400 МБ до 1,61 ГБ.

По истечении начальной задержки переход к страницам 49999, 49998, 49997 и т. Д. Происходит мгновенно, а перемещение назад и вперед между страницами 1–50000 также мгновенно.

Я могу только предположить, что весь набор данных каким-то образом загружен в память.

Дополнительные примечания:

  1. MetadataId устанавливается в качестве первичного ключа.
  2. Другие столбцы с возможностью поиска, такие как DocumentType, InvoiceNumber и т. Д., Также индексируются, но не уникальны.
  3. Мне нужно продолжать использовать динамическую хранимую процедуру по разным причинам, но главная из них заключается в том, что, хотя требования к полю меняются от клиента к клиенту, результат, используемый нашим приложением, остается неизменным.
  4. Использование редакции SQL Server 2014 для моих тестов.

Итак, мои вопросы:

  1. Может кто-нибудь объяснить мне, что на самом деле происходит? Все ли данные загружаются в память?

  2. Есть ли способ улучшить это? Обратите внимание, что мне нужно Row_ID сгенерировать с помощью 'ROW_NUMBER () OVER', так как часть предложения WHERE в моем операторе SQL может довольно сильно измениться в зависимости от того, под каким параметром ищет пользователь.

Спасибо.

UPDATE-1

Вот план выполнения:

Execution Plan

Ответы [ 2 ]

0 голосов
/ 20 февраля 2019

Я полагаю, что для столбцов, используемых в запросе, должен быть некластеризованный составной индекс, чтобы все столбцы таблицы в запросе были отсортированы вместе и указывали на metedata_Id pk; или представление, созданное с использованием отфильтрованного индекса в предложении where запроса. Ожидается потребление дискового пространства для хранения. У меня есть опасение, что есть предложение order by с использованием row_id, которое не является частью столбцов запроса select, а переименовано в столбец ... хотя должна была быть ошибка.

0 голосов
/ 06 сентября 2018

Динамический sql - это хорошо, надеюсь, вы используете sp_executesql.

Row_ID BETWEEN 1 AND 20, then 21 to 40 then 41 to 60, the results are instant

Поскольку при выполнении первого запроса план представляет собой кэш, который повторно используется последующим запросом между row_id между 1 и 20, затем с 21 по 40, затем с 41 по 60.

Row_ID BETWEEN 999981 AND 1000000, it takes 28/29 secs for to load

Я полагаю, оптимизатор запросов создает новый план для этих диапазонов, поэтому выполнение в первый раз занимает больше времени, чем планирование в кеше.

в следующий раз row_id 999980 - 999960 выполняются быстрее, поскольку он повторно использует план.

Я думаю, что у него есть проблема с прослушиванием параметров.

Я думаю, у вашего запроса есть возможности для оптимизации, но я не могу сказать, не глядя на него.

OFFSET / FETCH может улучшить ваш запрос, поскольку сократит один оператор Select, но не уверен, что он полностью зависит от основного запроса.

Этого большого плана недостаточно. Сканирование кластерного индекса не всегда плохо.

...