Курсор MSSQL для огромных таблиц не может выделить место - PullRequest
2 голосов
/ 02 апреля 2009

Мы пытаемся настроить курсор для запуска записей, сгенерированных из объединения двух «экземпляров» одной и той же огромной таблицы (более 150 млн записей).

Появляется следующее сообщение об исключении:

Не удалось выделить место для объекта 'dbo.SORT временное хранилище: 165282123350016' в базе данных 'tempdb', поскольку файловая группа 'PRIMARY' заполнена. Создайте место на диске, удалив ненужные файлы, удалив объекты в файловой группе, добавив дополнительные файлы в файловую группу или установив автоматический рост для существующих файлов в файловой группе.

Кто-нибудь из вас знает причину этого? Или как сделать приведенный ниже запрос более эффективным?

Я обнаружил, что это происходит где-то между DECLARE CURSOR и первым FETCH NEXT, но я пока не знаю, находится ли он между ...

  • DECLARE CURSOR и OPEN

или между

  • OPEN и первый FETCH NEXT.

Подробнее: оператор SQL выглядит так:

DECLARE cData CURSOR LOCAL FORWARD_ONLY READ_ONLY
FOR
  SELECT ...
  FROM HugeTable HT1 JOIN HugeTable HT2 ON .. 
  JOIN Table3 ON .. JOIN Table4 ON .. JOIN Table5 ON ..
  WHERE ...
  ORDER BY HT1..., HT1...

INSERT INTO SysLog (Description) VALUES ('A')

OPEN cData
BEGIN TRANSACTION ProcessData
  -- Currently trying new logging here:
  -- INSERT INTO SysLog (Description) VALUES ('B') 
  FETCH NEXT FROM cData INTO ...
  INSERT INTO SysLog (Description) VALUES ('C')
  ... etc.

где последнее сообщение журнала, которое я получаю, - «A», а затем через час оно завершается с сообщением, описанным выше, никогда не достигая «C». Сейчас я пытаюсь войти в систему в точке «B».


По запросу выкладываю точное выражение sql:

DECLARE cSource CURSOR LOCAL FORWARD_ONLY READ_ONLY
FOR
    SELECT MD.sFieldName, 
        MD.sFieldValue, 
        TR.sTargetDataType,
        MD2.sFieldValue AS sUniqueID,
        TR.sTargetTableName,
        TR.sTargetFieldName,
        I.iRefCustomerID, 
        I.iInterfaceID, 
        IL.iRefInterfaceSessionID
    FROM MasterData MD
    JOIN MasterData MD2
        ON MD.iRowIndex = MD2.iRowIndex
        AND MD.iBatchNumber = MD2.iBatchNumber
        AND MD.sTableName = MD2.sTableName 
        AND MD2.sFieldName = 'sUniqueID'
    JOIN SourceTargetRelation TR
        ON MD.sFieldName = TR.sSourceFieldName
        AND MD.sTableName = TR.sSourceTableName
    JOIN InterfaceLog IL
        ON IL.iInterfaceLogID = MD.iBatchNumber
    JOIN Interface I
        ON I.iInterfaceID = IL.iRefInterfaceID
        AND TR.iRefSystemID = I.iRefSystemID
    WHERE
        MD.iBatchNumber = @iBatchNumber
    ORDER BY MD.sTableName, MD.iRowIndex

После обновленного ответа от Quassnoi я также публикую исходный индекс в таблице:

У меня есть некластеризованный индекс для этой таблицы со столбцами iBatchNumber, sFieldName, sTableName, iRowIndex. И этот индекс имеет sFieldValue в качестве включенного столбца.


Как предложил Кассной (и теперь я понимаю, почему), я изменил индекс, чтобы столбцы были в следующем порядке: iBatchNumber, sTableName, iRowIndex, sFieldName. И я использую sFieldValue в качестве включенного столбца. План выполнения больше не содержит SORT, а количество шагов в плане выполнения составляет менее половины исходного, что, я надеюсь, также быстрее ...

Ответы [ 2 ]

7 голосов
/ 02 апреля 2009

Кто-нибудь из вас знает причину этого? Или как сделать приведенный ниже запрос более эффективным?

Ваш запрос использует ORDER BY.

Для этой сортировки требуется временное пространство. Вы вне этого пространства.

Чтобы избежать этого, создайте составной индекс для своей огромной таблицы: (col_filter_1, col_filter_2, col_order_1, col_order_2), где col_filter_n - столбцы, по которым вы фильтруете, а col_order_n - столбцы, по которым вы упорядочиваете.

Такой индекс можно использовать как для фильтрации, так и для упорядочения отфильтрованных результатов.

Если вы опубликуете свой фактический запрос (то есть выражения, по которым вы фильтруете и упорядочите их), я, вероятно, расскажу вам более точно, как создать такой индекс.

Обновление:

Из вашего запроса я вижу, что вам нужен индекс для (iBatchNumber, sTableName, iRowIndex, sFieldName) (в таком порядке).

Также может помочь, если вы сделаете MD2 ведущим в объединении:

WHERE
    MD2.iBatchNumber = @iBatchNumber
ORDER BY
    MD2.sTableName, MD2.iRowIndex

См. План выполнения и убедитесь, что SORT операция не используется.

0 голосов
/ 02 апреля 2009

Почему вы используете курсор? Особенно на большом столе? Что вы делаете, что не может быть сделано на основе набора? Cursprs чрезвычайно плох для производительности и не должны использоваться, если существует другая альтернатива. Если вы вставляете в другую таблицу на основе записей, найденных вашим выбором, это можно сделать намного лучше без курсора.

...