En un servidor con 32 GB, estamos ejecutando SQL Server 2014 SP2 con una memoria máxima de 25 GB, tenemos dos tablas, aquí encontrará una estructura simplificada de ambas tablas:
CREATE TABLE [dbo].[Settings](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceId] [int] NULL,
[typeID] [int] NULL,
[remark] [varchar](max) NULL,
CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Resources](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceUID] [int] NULL,
CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
con los siguientes índices no agrupados:
CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
[resourceUID] ASC
)
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
[resourceId] ASC,
[typeID] ASC
)
La base de datos está configurada con compatibility level
120.
Cuando ejecuto esta consulta hay derrames a tempdb
. Así es como ejecuto la consulta:
exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Si no selecciona el [remark]
campo, no se producen derrames. Mi primera reacción fue que los derrames ocurrieron debido al bajo número de filas estimadas en el operador de bucle anidado.
Entonces agrego 5 columnas datetime y 5 enteras a la tabla de configuración y las agrego a mi declaración select. Cuando ejecuto la consulta, no se producen derrames.
¿Por qué los derrames solo ocurren cuando [remark]
se selecciona? Probablemente tiene algo que ver con el hecho de que esto es un varchar(max)
. ¿Qué puedo hacer para evitar derramar tempdb
?
Agregar OPTION (RECOMPILE)
a la consulta no hace ninguna diferencia.
fuente
select r.id, LEFT(remark, 512)
(o cualquier longitud de subcadena razonable).Respuestas:
Habrá varias soluciones posibles aquí.
Puede ajustar manualmente la concesión de memoria, aunque probablemente no iría por esa ruta.
También puede usar un CTE y TOP para empujar la clasificación hacia abajo, antes de agarrar la columna de longitud máxima. Se verá algo así como a continuación.
Prueba de concepto dbfiddle aquí . ¡Aún se apreciarán datos de muestra!
Si desea leer un excelente análisis de Paul White, lea aquí.
fuente
El derrame ocurre cuando incluye esa columna porque no obtiene una concesión de memoria lo suficientemente grande para los datos de cadena grandes que se ordenan.
No obtiene una concesión de memoria lo suficientemente grande porque el número real de filas es 10 veces más que el número estimado de filas (1.302 reales frente a 126 estimados).
¿Por qué está fuera de la estimación? ¿Por qué SQL Server cree que solo hay una fila en dbo.Settings con un
resourceid
de 38?Podría ser un problema de estadísticas, que puede verificar ejecutando
DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')
y ver los recuentos para ese paso del histograma. Pero el plan de ejecución parece indicar que las estadísticas son tan completas y actualizadas como podrían ser.Dado que las estadísticas no ayudan, su mejor opción es probablemente una reescritura de consultas, que Forrest ha cubierto en su respuesta.
fuente
Para mí, parece que la
where
cláusula en la consulta está dando el problema, y es la causa de las bajas estimaciones, incluso siOPTION(RECOMPILE)
se usa.Creé algunos datos de prueba, y al final se me ocurrieron dos soluciones, almacenando el
ID
camporesources
en una variable (si siempre es única) o en una tabla temporal, si podemos tener más de unaID
.Base de registros de prueba
Inserte los valores de 'Búsqueda' para obtener el mismo conjunto de resultados aproximado que OP (1300 registros)
Cambie las estadísticas de compatibilidad y actualización para que coincidan con el OP
Consulta original
Mis estimaciones son aún peores , con una fila estimada, mientras que se devuelven 1300. Y como OP dijo, no importa si agrego
OPTION(RECOMPILE)
Una cosa importante a tener en cuenta es que cuando nos deshacemos de la cláusula where, las estimaciones son 100% correctas, lo que se espera ya que estamos utilizando todos los datos en ambas tablas.
Forcé los índices solo para asegurarme de que usamos los mismos que en la consulta anterior, para probar el punto
Como era de esperar, buenas estimaciones.
Entonces, ¿qué podríamos cambiar para obtener mejores estimaciones pero aún buscar nuestros valores?
SI @UID es único, como en el ejemplo que dio OP, podríamos poner el single
id
que se devolvióresources
en una variable, luego buscar esa variable con una OPTION (RECOMPILE)Lo que da estimaciones 100% precisas
Pero, ¿qué pasa si hay múltiples resourceUID en los recursos?
agregar algunos datos de prueba
Esto podría resolverse con una tabla temporal
De nuevo con estimaciones precisas .
Esto se hizo con mi propio conjunto de datos, YMMV.
Escrito con sp_executesql
Con una variable
Con una mesa temporal
Todavía estimaciones 100% correctas en mi prueba
fuente