Мое предложение IN приводит к полному сканированию индекса в T-SQL. Что я могу сделать? - PullRequest
2 голосов
/ 26 марта 2009

У меня есть SQL-запрос с 50 параметрами, такими как этот.

DECLARE
  @p0 int, @p1 int, @p2 int, (text omitted), @p49 int

SELECT
  @p0=111227, @p1=146599, @p2=98917, (text omitted), @p49=125319

--
SELECT
  [t0].[CustomerID], [t0].[Amount],
  [t0].[OrderID], [t0].[InvoiceNumber]
FROM [dbo].[Orders] AS [t0]
WHERE ([t0].[CustomerID]) IN
  (@p0, @p1, @p2, (text omitted), @p49)

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

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

Поскольку это выполняется в цикле со стороны клиента (каждый раз отправляется 50 параметров, для 1000 итераций ), это плохая ситуация. Как я могу сформулировать код на стороне запроса / клиента, чтобы получить мои данные без повторного сканирования индекса при одновременном уменьшении количества обращений?


Я думал о том, чтобы упорядочить параметры по 50 тыс., Чтобы были меньшие значения индекса. Есть причудливое смягчающее обстоятельство, которое мешает этому - я не могу использовать это решение. Чтобы смоделировать это обстоятельство, просто предположим, что у меня есть только 50 идентификаторов, доступных в любое время, и я не могу контролировать их относительное положение в глобальном списке.

Ответы [ 3 ]

7 голосов
/ 26 марта 2009

Вставьте параметры во временную таблицу, затем объедините ее со своей таблицей:

DECLARE @params AS TABLE(param INT);

INSERT
INTO    @params
VALUES  (@p1)
...
INSERT
INTO    @params
VALUES  (@p49)

SELECT
  [t0].[CustomerID], [t0].[Amount],
  [t0].[OrderID], [t0].[InvoiceNumber]
FROM @params, [dbo].[Orders] AS [t0]
WHERE ([t0].[CustomerID]) = @params.param

Это, скорее всего, будет использовать NESTED LOOPS с INDEX SEEK над CustomerID в каждом цикле.

1 голос
/ 27 марта 2009

Если исходить из ответа Quassnoi, если вы работаете с SQL 2008, вы можете сэкономить некоторое время, вставив все 50 элементов одним оператором. В SQL 2008 появилась новая функция для многозначных вставок. например,

INSERT INTO @Customers (CustID)
VALUES (@p0),
       (@p1),
       <snip>
       (@p49)

Теперь таблица @Customers заполнена и готова к включению INNER JOIN или вашему предложению IN.

1 голос
/ 26 марта 2009

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

Я не могу винить вас за то, что вы хотите сэкономить на поездках на сервер, поместив каждый из идентификаторов, которые вы ищете, в пакет. Если сканирование индекса RANGE действительно беспокоит вас, вы можете создать параметризованный курсор на стороне сервера (например, в TSQL), который принимает CustomerID в качестве параметра. Остановитесь, как только найдете совпадение. Этот запрос должен определенно использовать сканирование уникального индекса вместо сканирования диапазона.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...