Para mim, parece que a where
cláusula na consulta está fornecendo o problema e é a causa das baixas estimativas, mesmo que OPTION(RECOMPILE)
sejam usadas.
Criei alguns dados de teste e, no final, criei duas soluções, armazenando o ID
campo resources
em uma variável (se sempre for única) ou em uma tabela temporária, se pudermos ter mais de uma ID
.
Registros de teste base
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
Insira os valores 'Procurar', para obter o mesmo 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
Altere as estatísticas compat & Update para corresponder ao 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
Minhas estimativas são ainda piores , com uma linha estimada, enquanto 1300 são retornadas. E, como o OP afirmou, não importa se eu adicionarOPTION(RECOMPILE)
Uma coisa importante a ser observada é que, quando nos livramos da cláusula where, as estimativas estão 100% corretas, o que é esperado, pois estamos usando todos os dados nas duas tabelas.
Forcei os índices apenas para garantir que usamos os mesmos da consulta anterior, para provar o ponto
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 esperado, boas estimativas.
Então, o que poderíamos mudar para obter melhores estimativas, mas ainda buscar nossos valores?
Se @UID for único, como no exemplo que o OP deu, poderíamos colocar o único id
retornado resources
em uma variável e procurar nessa variável com um 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);
O que fornece estimativas 100% precisas
Mas e se houver vários resourceUIDs em recursos?
adicione alguns dados de teste
INSERT INTO Resources(ResourceUID)
VALUES (38);
go 50
Isso pode ser resolvido com uma tabela temporária
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
Novamente com estimativas precisas .
Isso foi feito com meu próprio conjunto de dados, YMMV.
Escrito com sp_executesql
Com uma variável
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
Com uma tabela temporária
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
Ainda estimativas 100% corretas no meu teste
select r.id, LEFT(remark, 512)
(ou qualquer que seja o tamanho sensível da substring).