Para mí, parece que la where
cláusula en la consulta está dando el problema, y es la causa de las bajas estimaciones, incluso si OPTION(RECOMPILE)
se usa.
Creé algunos datos de prueba, y al final se me ocurrieron dos soluciones, almacenando el ID
campo resources
en una variable (si siempre es única) o en una tabla temporal, si podemos tener más de una ID
.
Base de registros de prueba
SET NOCOUNT ON
DECLARE @i int= 1;
WHILE @i <= 10000
BEGIN
INSERT INTO [dbo].[Settings]([resourceId],[typeID],remark)
VALUES(@i,@i,'KEPT THESE VALUES OUT BECAUSE IT WOULD CLUTTER THE EXAMPLES, VALUES OVER 8000 Chars entered here'); -- 23254 character length on each value
INSERT INTO [dbo].[Resources](resourceUID)
VALUES(@i);
SET @i += 1;
END
Inserte los valores de 'Búsqueda' para obtener el mismo conjunto de resultados aproximado que OP (1300 registros)
INSERT INTO [dbo].[Settings]([resourceId],[typeID],remark)
VALUES(38,38,'KEPT THESE VALUES OUT BECAUSE IT WOULD CLUTTER THE EXAMPLES, VALUES OVER 8000 Chars entered here')
GO 1300
Cambie las estadísticas de compatibilidad y actualización para que coincidan con el OP
ALTER DATABASE StackOverflow SET COMPATIBILITY_LEVEL = 120;
UPDATE STATISTICS settings WITH FULLSCAN;
UPDATE STATISTICS resources WITH FULLSCAN;
Consulta original
exec sp_executesql N'
select r.id
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Mis estimaciones son aún peores , con una fila estimada, mientras que se devuelven 1300. Y como OP dijo, no importa si agregoOPTION(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
exec sp_executesql N'
select r.id,remark
FROM Resources r with(index([IX_UID]))
inner join Settings WITH(INDEX([IX_Test]))
on resourceid=r.id
ORDER BY typeID',
N'@UID int',
@UID=38
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)
DECLARE @UID int =38 , @RID int;
SELECT @RID=r.id from
Resources r where resourceUID = @UID;
SELECT @uid, remark
from Settings
where resourceId = @uid
Order by typeID
OPTION(RECOMPILE);
Lo que da estimaciones 100% precisas
Pero, ¿qué pasa si hay múltiples resourceUID en los recursos?
agregar algunos datos de prueba
INSERT INTO Resources(ResourceUID)
VALUES (38);
go 50
Esto podría resolverse con una tabla temporal
CREATE TABLE #RID (id int)
DECLARE @UID int =38
INSERT INTO #RID
SELECT r.id
from
Resources r where resourceUID = @UID
SELECT @uid, remark
from Settings s
INNER JOIN #RID r
ON r.id =s.resourceId
Order by typeID
OPTION(RECOMPILE)
DROP TABLE #RID
De nuevo con estimaciones precisas .
Esto se hizo con mi propio conjunto de datos, YMMV.
Escrito con sp_executesql
Con una variable
exec sp_executesql N'
DECLARE @RID int;
SELECT @RID=r.id from
Resources r where resourceUID = @UID;
SELECT @uid, remark
from Settings
where resourceId = @uid
Order by typeID
OPTION(RECOMPILE);',
N'@UID int',
@UID=38
Con una mesa temporal
exec sp_executesql N'
CREATE TABLE #RID (id int)
INSERT INTO #RID
SELECT r.id
from
Resources r where resourceUID = @UID
SELECT @uid, remark
from Settings s
INNER JOIN #RID r
ON r.id =s.resourceId
Order by typeID
OPTION(RECOMPILE)
DROP TABLE #RID',
N'@UID int',
@UID=38
Todavía estimaciones 100% correctas en mi prueba
select r.id, LEFT(remark, 512)
(o cualquier longitud de subcadena razonable).