Error de desbordamiento de fila de 8k al actualizar una fila de tamaño 5k

8

Estoy tratando de actualizar una tabla de destino que tiene una fila de tamaño 5k a una fila de tamaño 5k también.

Como es una fila, es fácil saber el tamaño real de la fila:

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')

Reproducir

La tabla no fue alterada desde la creación. No veo ninguna razón por la que debería fallar. Ideas?

Yosi Dahari
fuente
2
Similar a tu última pregunta . Este error se produce al crear una mesa de trabajo para un tipo también i.stack.imgur.com/wenSE.png , i.stack.imgur.com/MVyXf.png
Martin Smith

Respuestas:

9

El problema está relacionado con el hecho de que está actualizando la clave de agrupación y la tabla de destino tiene un esquema de partición 1 . Cuando se solicita a SQL Server que actualice cualquier componente de la clave de agrupación, debe realizar una actualización híbrida UPDATEy DELETE/ o híbrida donde algunas de las filas se actualizan in situ y otras no.

Si elimina el índice agrupado de la tabla de destino, verá que la actualización funciona.

El mensaje de error, aunque quizás sea un poco engañoso, es preciso ya que el tamaño de la fila resultante durante la actualización excede la longitud máxima.

Le sugiero que considere cambiar la estructura de la tabla a:

  • No utilizar VARCHAR(MAX)para todas esas columnas. Si en realidad no necesita 2 GB de caracteres en una sola columna, ¿por qué definir la columna de esa manera? Defina la columna como el tamaño máximo que se encontrará de manera realista.
  • quizás divida esta tabla en varias tablas en las que el tamaño máximo de fila resultante sea inferior a 8060 bytes. Parece que tienes varias agrupaciones lógicas de columnas, como los V_MAX_xxx, V_64_xxxy V_512_xxxcolumnas, etc.

Para simplificar su repro, es posible que desee eliminar el cursor y solo realizar la siguiente operación DML:

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

La columna anterior es uno de los componentes de la clave de agrupación y también la clave de partición (la actualización de otras columnas de clave CI funciona bien).

Con el índice agrupado en su lugar, obtiene este error:

Mensaje 511, Nivel 16, Estado 1, Línea 1

No se puede crear una fila de tamaño 8287 que sea mayor que el tamaño de fila máximo permitido de 8060.

La instrucción se ha terminado.

Sin el índice agrupado en su lugar, la declaración tiene éxito.


1 Curiosamente, si eliminamos la partición de la reproducción, encontramos que la actualización se realiza correctamente, incluso con el índice agrupado en su lugar.

Max Vernon
fuente
1
Creo que estamos en camino a una solución aquí. Básicamente, el tiempo de muestreo es el único que tengo que cambiar, y está en el índice agrupado solo porque la tabla está particionada por él. Entonces, una solución será cambiar la forma en que se divide la tabla (lo cual es doloroso, pero posible), y luego el índice agrupado
Yosi Dahari
10

La actualización falla por razones muy similares a las que expliqué en respuesta a su pregunta anterior .

En este caso, debido a que potencialmente está actualizando varias filas donde se cambia una columna clave de un índice único * , SQL Server crea un plan que incluye operadores de división, clasificación y colapso para evitar violaciones de clave únicas intermedias (consulte este artículo para más detalles) .

El operador de clasificación así introducido encuentra una fila intermedia (incluidos los gastos generales internos) de un ancho que excede el límite, por lo que se genera un error. Agregar una OPTION (ROBUST PLAN)pista a la consulta de actualización muestra que esto es inevitable:

Mensaje 8619, Nivel 16, Estado 2, Línea 681
El procesador de consultas no pudo generar un plan de consulta porque se requiere una mesa de trabajo y su tamaño mínimo de fila excede el máximo permitido de 8060 bytes. Una razón típica por la que se requiere una mesa de trabajo es una cláusula GROUP BY u ORDER BY en la consulta. Vuelva a enviar su consulta sin la sugerencia de ROBUST PLAN.

Las relaciones de datos de origen / destino no están claras para mí desde un breve vistazo, pero si puede garantizar que cada operación de actualización afectará a lo más una fila, puede evitar la necesidad de dividir / ordenar / contraer agregando TOP (1)a la declaración de actualización:

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

Sin embargo, esto es un poco hack. Idealmente, la construcción de la declaración de actualización y los índices deberían proporcionar suficiente información al optimizador para que pueda ver que, como máximo, se actualizará una fila. En particular, es una buena práctica escribir declaraciones de actualización que sean deterministas .

Dado el diseño extraño y la falta de claridad en la pregunta, ni siquiera voy a tratar de descifrar las relaciones de datos, o los cambios de consulta e índice que serían necesarios para lograr esto en detalle.

* Como Martin Smith señaló en un comentario, esto no sería un problema en esta situación particular si la tabla no estuviera particionada. Cuando la actualización establece la clave en el mismo valor determinista en cada fila, no es necesario dividir / ordenar / contraer, a menos que la tabla también esté particionada en esa clave. Entonces, una solución alternativa para esta consulta es no particionar la tabla en tiempo de muestreo .

Paul White 9
fuente