Estoy bastante seguro de que las definiciones de tabla están cerca de esto:
CREATE TABLE dbo.households
(
tempId integer NOT NULL,
n integer NOT NULL,
HHID integer IDENTITY NOT NULL,
CONSTRAINT [UQ dbo.households HHID]
UNIQUE NONCLUSTERED (HHID),
CONSTRAINT [PK dbo.households tempId, n]
PRIMARY KEY CLUSTERED (tempId, n)
);
CREATE TABLE dbo.persons
(
tempId integer NOT NULL,
sporder integer NOT NULL,
n integer NOT NULL,
PERID integer IDENTITY NOT NULL,
HHID integer NOT NULL,
CONSTRAINT [UQ dbo.persons HHID]
UNIQUE NONCLUSTERED (PERID),
CONSTRAINT [PK dbo.persons tempId, n, sporder]
PRIMARY KEY CLUSTERED (tempId, n, sporder)
);
No tengo estadísticas para estas tablas o sus datos, pero lo siguiente al menos establecerá la cardinalidad de la tabla correcta (los recuentos de páginas son una suposición):
UPDATE STATISTICS dbo.persons
WITH
ROWCOUNT = 5239842,
PAGECOUNT = 100000;
UPDATE STATISTICS dbo.households
WITH
ROWCOUNT = 1928783,
PAGECOUNT = 25000;
Análisis del plan de consulta
La consulta que tiene ahora es:
UPDATE P
SET HHID = H.HHID
FROM dbo.households AS H
JOIN dbo.persons AS P
ON P.tempId = H.tempId
AND P.n = H.n;
Esto genera el plan bastante ineficiente:
Los principales problemas en este plan son el hash join y sort. Ambos requieren una concesión de memoria (la combinación hash necesita construir una tabla hash, y la ordenación necesita espacio para almacenar las filas mientras la ordenación progresa). Plan Explorer muestra que esta consulta recibió 765 MB:
¡Esto es bastante memoria del servidor para dedicar a una consulta! Más concretamente, esta concesión de memoria se repara antes de que comience la ejecución en función del recuento de filas y las estimaciones de tamaño.
Si la memoria resulta insuficiente en el momento de la ejecución, al menos algunos datos para el hash y / o la clasificación se escribirán en el disco físico tempdb . Esto se conoce como 'derrame' y puede ser una operación muy lenta. Puede rastrear estos derrames (en SQL Server 2008) utilizando los eventos Profiler Hash Warnings y Sort Warnings .
La estimación de la entrada de compilación de la tabla hash es muy buena:
La estimación para la entrada de clasificación es menos precisa:
Tendría que usar Profiler para verificar, pero sospecho que el tipo se derramará a tempdb en este caso. También es posible que la tabla hash se derrame también, pero eso es menos claro.
Tenga en cuenta que la memoria reservada para esta consulta se divide entre la tabla hash y la clasificación, porque se ejecutan simultáneamente. La propiedad del plan Fracciones de memoria muestra la cantidad relativa de la concesión de memoria que se espera que utilice cada operación.
¿Por qué ordenar y hacer picadillo?
El optimizador de consultas introduce la clasificación para garantizar que las filas lleguen al operador de Actualización de índice agrupado en orden de clave agrupado. Esto promueve el acceso secuencial a la tabla, que a menudo es mucho más eficiente que el acceso aleatorio.
La combinación hash es una opción menos obvia, porque sus entradas son de tamaños similares (de todos modos, para una primera aproximación). La combinación de hash es mejor cuando una entrada (la que construye la tabla hash) es relativamente pequeña.
En este caso, el modelo de costos del optimizador determina que la combinación hash es la más barata de las tres opciones (hash, merge, loops anidados).
Mejorando el desempeño
El modelo de costos no siempre lo hace bien. Tiende a sobreestimar el costo de la combinación de combinación paralela, especialmente a medida que aumenta el número de subprocesos. Podemos forzar una combinación de combinación con una sugerencia de consulta:
UPDATE P
SET HHID = H.HHID
FROM dbo.households AS H
JOIN dbo.persons AS P
ON P.tempId = H.tempId
AND P.n = H.n
OPTION (MERGE JOIN);
Esto produce un plan que no requiere tanta memoria (porque la combinación de combinación no necesita una tabla hash):
El tipo problemático sigue ahí, porque la combinación de combinación solo conserva el orden de sus claves de combinación (tempId, n), pero las claves agrupadas son (tempId, n, sporder). Es posible que el plan de combinación de combinación no funcione mejor que el plan de combinación hash.
Los bucles anidados se unen
También podemos probar una unión de bucles anidados:
UPDATE P
SET HHID = H.HHID
FROM dbo.households AS H
JOIN dbo.persons AS P
ON P.tempId = H.tempId
AND P.n = H.n
OPTION (LOOP JOIN);
El plan para esta consulta es:
Este plan de consulta es considerado el peor por el modelo de costos del optimizador, pero tiene algunas características muy deseables. Primero, la unión de bucles anidados no requiere una concesión de memoria. En segundo lugar, puede preservar el orden de las claves de la Persons
tabla para que no se necesite una ordenación explícita. Es posible que este plan funcione relativamente bien, tal vez incluso lo suficientemente bueno.
Bucles anidados paralelos
El gran inconveniente con el plan de bucles anidados es que se ejecuta en un solo hilo. Es probable que esta consulta se beneficie del paralelismo, pero el optimizador decide que no hay ninguna ventaja en hacerlo aquí. Esto tampoco es necesariamente correcto. Desafortunadamente, no hay una sugerencia de consulta integrada para obtener un plan paralelo, pero hay una forma no documentada:
UPDATE t1
SET t1.HHID = t2.HHID
FROM dbo.persons AS t1
INNER JOIN dbo.households AS t2
ON t1.tempId = t2.tempId AND t1.n = t2.n
OPTION (LOOP JOIN, QUERYTRACEON 8649);
Habilitar el indicador de seguimiento 8649 con la QUERYTRACEON
sugerencia produce este plan:
Ahora tenemos un plan que evita la clasificación, no requiere memoria adicional para la unión y utiliza el paralelismo de manera efectiva. Debería encontrar que esta consulta funciona mucho mejor que las alternativas.
Más información sobre paralelismo en mi artículo Forzar un plan de ejecución de consultas paralelas :