Ошибка переполнения строки 8 КБ при обновлении строки размером 5 КБ

8

Я пытаюсь обновить целевую таблицу, которая имеет одну строку размером 5 КБ и строку размером 5 КБ.

Поскольку это одна строка, легко узнать фактический размер строки:

select *
from sys.dm_db_index_physical_stats(DB_ID('RODS_HSD_ES'), 
OBJECT_ID(N'TBL_BM_HSD_SUBJECT_AN_148_REPRO'), NULL, NULL, 'DETAILED')

воспроизводить

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

Йоси Дахари
источник
2
Похоже на ваш последний вопрос . Эта ошибка при создании рабочего стола
Мартин Смит,

Ответы:

9

Проблема связана с тем, что вы обновляете ключ кластеризации, и у таблицы назначения есть схема разделения 1 . Когда SQL Server запрашивается обновить любой компонент ключа кластеризации, он должен выполнитьUPDATEDELETE гибридное обновление и , или, где некоторые строки обновляются на месте, а некоторые нет.

Если вы удалите кластерный индекс из таблицы назначения, вы увидите, что обновление работает.

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

Я предлагаю вам рассмотреть вопрос об изменении структуры таблицы:

  • не использовать VARCHAR(MAX)для всех этих столбцов. Если вам на самом деле не нужно 2 ГБ символов в одном столбце, зачем определять столбец таким образом? Определите столбец как максимальный размер, который будет реально встречаться.
  • возможно, разбить эту таблицу на несколько таблиц, в которых максимальный размер строки будет меньше 8060 байт. Кажется , у вас есть несколько логических кластеров колонн, такие как V_MAX_xxx, V_64_xxxи V_512_xxxколонны, и т.д.

Чтобы упростить ваше воспроизведение, вы можете удалить курсор и выполнить только следующую операцию DML:

UPDATE dbo.TBL_BM_HSD_SUBJECT_AN_148_REPRO_TARGET
SET [sampletime]  = '2015-12-29 01:11:26.687';

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

С кластеризованным индексом вы получите эту ошибку:

Сообщение 511, уровень 16, состояние 1, строка 1

Невозможно создать строку размером 8287, которая превышает максимально допустимый размер строки 8060.

Заявление было прекращено.

Без кластерного индекса оператор выполняется успешно.


1 Интересно, что если мы исключим разделение из репродукции, мы обнаружим, что обновление прошло успешно даже при наличии кластерного индекса.

Макс Вернон
источник
1
Я думаю, что мы находимся на пути к решению здесь. По сути, единственное, что мне нужно изменить, - это примерное время, и оно входит в кластеризованный индекс только потому, что таблица разделена им. Таким образом, решение будет состоять в том, чтобы изменить способ разделения таблицы (что болезненно, но возможно), а затем кластерный индекс
Йоси Дахари
10

Обновление не выполняется по причинам, сходным с теми, которые я объяснил в ответ на ваш предыдущий вопрос .

В этом случае, потому что вы потенциально обновление нескольких строк , где ключевой столбец из уникального индекса является изменен * , SQL Server создает план , который включает в себя Split, сортировке и оператор Collapse , чтобы избежать промежуточных уникальных нарушений основных (см этой статьи для более подробной информации) ,

Введенный таким образом оператор сортировки встречает промежуточную строку (включая внутренние издержки) шириной, превышающей предел, поэтому возникает ошибка. Добавление OPTION (ROBUST PLAN)подсказки к запросу на обновление показывает, что это неизбежно:

Сообщение 8619, уровень 16, состояние 2, строка 681
Обработчику запросов не удалось создать план запроса, поскольку требуется рабочая таблица, а ее минимальный размер строки превышает максимально допустимый размер в 8060 байтов. Типичная причина, по которой требуется рабочая таблица, - это предложение GROUP BY или ORDER BY в запросе. Повторите ваш запрос без подсказки ROBUST PLAN.

Отношения источника и цели данных неясны для меня из краткого обзора, но если вы можете гарантировать, что каждая операция обновления повлияет не более чем на одну строку, вы можете избежать необходимости разделения / сортировки / свертывания, добавив TOP (1)в оператор обновления:

UPDATE TOP (1) [TBL_BM_HSD_SUBJECT_AN_148_REPRO_TARGET] 
SET ...

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

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

* Как отметил Мартин Смит в комментарии, это не было бы проблемой в данной конкретной ситуации, если бы таблица не была разделена. Если обновление устанавливает ключ в одно и то же детерминированное значение в каждой строке, Split / Sort / Collapse не требуется, если только таблица не разделена на этот ключ. Таким образом, альтернативное решение для этого запроса - не разбивать таблицу на время выборки .

Пол Уайт 9
источник