Actualizaciones únicas de índices y contadores de modificación de filas de estadísticas

14

Dada la siguiente tabla, índice agrupado único y estadísticas:

CREATE TABLE dbo.Banana
(
    pk integer NOT NULL, 
    c1 char(1) NOT NULL, 
    c2 char(1) NOT NULL
);

CREATE UNIQUE CLUSTERED INDEX pk ON dbo.Banana (pk);

CREATE STATISTICS c1 ON dbo.Banana (c1);
CREATE STATISTICS c2 ON dbo.Banana (c2);

INSERT dbo.Banana 
    (pk, c1, c2) 
VALUES 
    (1, 'A', 'W'), 
    (2, 'B', 'X'), 
    (3, 'C', 'Y'), 
    (4, 'D', 'Z');

-- Populate statistics
UPDATE STATISTICS dbo.Banana;

Datos de ejemplo

Los contadores de modificación de la fila de estadísticas obviamente muestran cero antes de cualquier actualización:

-- Show statistics modification counters
SELECT
    stats_name = S.[name], 
    DDSP.stats_id,
    DDSP.[rows],
    DDSP.modification_counter
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.object_id, S.stats_id) AS DDSP
WHERE
    S.[object_id] = OBJECT_ID(N'dbo.Banana', N'U');

Contadores de cero modificaciones

Incrementar cada pkvalor de columna en uno para cada fila:

-- Increment pk in every row
UPDATE dbo.Banana 
SET pk += 1;

Utiliza el plan de ejecución:

Plan de ejecución Split Sort Collapse

Produce los siguientes contadores de modificación de estadísticas:

Contadores de modificación posteriores a la actualización

Preguntas

  1. ¿Qué hacen los operadores Split, Sort y Collapse?
  2. ¿Por qué las pkestadísticas muestran 2 modificaciones, pero c1y c2muestran 5?
Paul White 9
fuente

Respuestas:

15

SQL Server siempre utiliza la combinación de operadores Dividir, Ordenar y Contraer cuando mantiene un índice único como parte de una actualización que afecta (o podría afectar) a más de una fila.

Trabajando a través del ejemplo en la pregunta, podríamos escribir la actualización como una actualización de una sola fila por separado para cada una de las cuatro filas presentes:

-- Per row updates
UPDATE dbo.Banana SET pk = 2 WHERE pk = 1;
UPDATE dbo.Banana SET pk = 3 WHERE pk = 2;
UPDATE dbo.Banana SET pk = 4 WHERE pk = 3;
UPDATE dbo.Banana SET pk = 5 WHERE pk = 4;

El problema es que la primera instrucción fallará, ya que cambia pkde 1 a 2, y ya hay una fila donde pk= 2. El motor de almacenamiento de SQL Server requiere que los índices únicos permanezcan únicos en cada etapa del procesamiento, incluso dentro de una sola instrucción . Este es el problema resuelto por Split, Sort y Collapse.

División División

El primer paso es dividir cada declaración de actualización en una eliminación seguida de una inserción:

DELETE dbo.Banana WHERE pk = 1;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');

DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');

DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');

DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

El operador Split agrega una columna de código de acción a la secuencia (aquí denominada Act1007):

Propiedades divididas

El código de acción es 1 para una actualización, 3 para una eliminación y 4 para una inserción.

Ordenar Ordenar

Las declaraciones divididas anteriores aún producirían una violación de clave única transitoria falsa, por lo que el siguiente paso es ordenar las declaraciones por las claves del índice único que se actualiza ( pken este caso), luego por el código de acción. Para este ejemplo, esto simplemente significa que las eliminaciones (3) en la misma clave se ordenan antes de las inserciones (4). El orden resultante es:

-- Sort (pk, action)
DELETE dbo.Banana WHERE pk = 1;
DELETE dbo.Banana WHERE pk = 2;
INSERT dbo.Banana (pk, c1, c2) VALUES (2, 'A', 'W');
DELETE dbo.Banana WHERE pk = 3;
INSERT dbo.Banana (pk, c1, c2) VALUES (3, 'B', 'X');
DELETE dbo.Banana WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (4, 'C', 'Y');
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Ordenar propiedades

Colapso Colapso

La etapa anterior es suficiente para garantizar la prevención de infracciones de unicidad falsas en todos los casos. Como optimización, Collapse combina eliminaciones e inserciones adyacentes en el mismo valor clave en una actualización:

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');

Los pares eliminar / insertar para los pkvalores 2, 3 y 4 se combinaron en una actualización, dejando una eliminación simple en pk= 1 y una inserción para pk= 5.

El operador Contraer agrupa filas por columnas clave y actualiza el código de acción para reflejar el resultado del colapso:

Propiedades de colapso

Actualización de índice agrupado Actualización de índice agrupado

Este operador está etiquetado como Actualización, pero es capaz de insertar, actualizar y eliminar. La acción que realiza la Actualización de índice agrupado por fila está determinada por el valor del código de acción en esa fila. El operador tiene una propiedad Action para reflejar este modo de operación:

Propiedad de acción Actualización de índice agrupado


Contadores de modificación de filas

Tenga en cuenta que las tres actualizaciones anteriores no modifican las claves del índice único que se mantiene. En efecto, hemos transformado las actualizaciones de las columnas clave en el índice en actualizaciones de las columnas no clave ( c1y c2), más una eliminación y una inserción. Ni una eliminación ni una inserción pueden causar una violación falsa de clave única.

Una inserción o una eliminación afecta a cada columna de la fila, por lo que las estadísticas asociadas con cada columna tendrán sus contadores de modificación incrementados. Para las actualizaciones, solo las estadísticas con cualquiera de las columnas actualizadas como columna inicial tienen sus contadores de modificación incrementados (incluso si el valor no ha cambiado).

Por lo tanto, los contadores de modificación de la fila de estadísticas muestran 2 cambios a pk, y 5 para c1y c2:

-- Collapse (pk)
DELETE dbo.Banana WHERE pk = 1;                         -- All columns modified
UPDATE dbo.Banana SET c1 = 'A', c2 = 'W' WHERE pk = 2;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'B', c2 = 'X' WHERE pk = 3;  -- c1 and c2 modified
UPDATE dbo.Banana SET c1 = 'C', c2 = 'Y' WHERE pk = 4;  -- c1 and c2 modified
INSERT dbo.Banana (pk, c1, c2) VALUES (5, 'D', 'Z');    -- All columns modified

Nota: Solo los cambios aplicados al objeto base (montón o índice agrupado) afectan a los contadores de modificación de filas de estadísticas. Los índices no agrupados son estructuras secundarias, que reflejan los cambios ya realizados en el objeto base. No afectan a los contadores de modificación de filas de estadísticas en absoluto.

Si un objeto tiene múltiples índices únicos, se usa una combinación separada de Split, Sort, Collapse para organizar las actualizaciones de cada uno. SQL Server optimiza este caso para índices no agrupados guardando el resultado de la división en un spool de tabla Eager, y luego vuelve a reproducir ese conjunto para cada índice único (que tendrá su propio Ordenar por teclas de índice + código de acción y Contraer).

Efecto sobre actualizaciones estadísticas

Las actualizaciones de estadísticas automáticas (si están habilitadas) ocurren cuando el optimizador de consultas necesita información estadística y nota que las estadísticas existentes están desactualizadas (o no son válidas debido a un cambio de esquema). Las estadísticas se consideran desactualizadas cuando el número de modificaciones registradas supera un umbral.

La disposición Split / Sort / Collapse da como resultado que se registren modificaciones de fila diferentes de las esperadas. Esto, a su vez, significa que una actualización de estadísticas puede activarse antes o después de lo que sería el caso de lo contrario.

En el ejemplo anterior, las modificaciones de fila para la columna clave aumentan en 2 (el cambio neto) en lugar de 4 (una para cada fila de la tabla afectada) o 5 (una para cada eliminación / actualización / inserción producida por el colapso).

Además, las columnas no clave que lógicamente no fueron modificadas por la consulta original acumulan modificaciones de fila, que pueden sumar el doble de las filas de la tabla actualizadas (una para cada eliminación y una para cada inserción).


El número de cambios registrados depende del grado de superposición entre los valores de columna de clave antiguos y nuevos (y, por lo tanto, el grado en que se pueden contraer las eliminaciones e inserciones separadas). Restableciendo la tabla entre cada ejecución, las siguientes consultas demuestran el efecto en los contadores de modificación de filas con diferentes superposiciones:

UPDATE dbo.Banana SET pk = pk + 0; -- Full overlap

pk = pk + 0

UPDATE dbo.Banana SET pk = pk + 1;

pk = pk + 1

UPDATE dbo.Banana SET pk = pk + 2;

pk = pk + 2

UPDATE dbo.Banana SET pk = pk + 3;

pk = pk + 3

UPDATE dbo.Banana SET pk = pk + 4; -- No overlap

pk = pk + 4

Paul White 9
fuente