Wydaje mi się, że where
klauzula w zapytaniu podaje problem i jest przyczyną niskich oszacowań, nawet jeśli OPTION(RECOMPILE)
jest używana.
Stworzyłem trochę danych testowych, a na koniec wymyśliłem dwa rozwiązania, przechowując ID
pole z resources
zmiennej (jeśli zawsze jest unikalna) lub tabeli tymczasowej, jeśli możemy mieć więcej niż jedną ID
.
Podstawowe zapisy testów
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
Wstaw wartości „Szukaj”, aby uzyskać ten sam przybliżony zestaw wyników, co OP (1300 rekordów)
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
Zmień statystyki kompatybilności i aktualizacji, aby dopasować OP
ALTER DATABASE StackOverflow SET COMPATIBILITY_LEVEL = 120;
UPDATE STATISTICS settings WITH FULLSCAN;
UPDATE STATISTICS resources WITH FULLSCAN;
Oryginalne zapytanie
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
Moje szacunki są jeszcze gorsze , z jednym szacowanym wierszem, a zwracanych jest 1300. I jak stwierdził OP, nie ma znaczenia, czy dodamOPTION(RECOMPILE)
Ważną rzeczą do zapamiętania jest to, że kiedy pozbyjemy się klauzuli where, szacunki są w 100% poprawne, co jest oczekiwane, ponieważ używamy wszystkich danych w obu tabelach.
Zmusiłem indeksy tylko po to, aby upewnić się, że używamy tych samych, co w poprzednim zapytaniu, aby to udowodnić
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
Zgodnie z oczekiwaniami dobre szacunki.
Co więc możemy zmienić, aby uzyskać lepsze szacunki, ale nadal szukać naszych wartości?
JEŻELI @UID jest unikalny, tak jak w przykładzie podanym przez OP, możemy umieścić singiel, id
który został zwrócony resources
w zmiennej, a następnie wyszukać tę zmienną z OPCJĄ (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);
Co daje 100% dokładne szacunki
Ale co, jeśli w zasobach znajduje się wiele zasobów UID?
dodaj trochę danych testowych
INSERT INTO Resources(ResourceUID)
VALUES (38);
go 50
Można to rozwiązać za pomocą tabeli tymczasowej
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
Ponownie z dokładnymi szacunkami .
Dokonano tego z moim własnym zestawem danych, YMMV.
Napisane za pomocą sp_executesql
Ze zmienną
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
Ze stołem tymczasowym
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
Wciąż 100% poprawne szacunki w moim teście
select r.id, LEFT(remark, 512)
(lub dowolną rozsądną długość podciągu).