对我来说,where
查询中的子句似乎引起了问题,并且是使用低估算值的原因OPTION(RECOMPILE)
。
我创建了一些测试数据,最后提出了两个解决方案,将ID
字段存储在resources
变量(如果始终是唯一的)或临时表中(如果可以有多个ID
的话)。
基本测试记录
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
插入“搜索”值,以获取与OP相同的近似结果集(1300条记录)
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
更改兼容性并更新统计信息以匹配OP
ALTER DATABASE StackOverflow SET COMPATIBILITY_LEVEL = 120;
UPDATE STATISTICS settings WITH FULLSCAN;
UPDATE STATISTICS resources WITH FULLSCAN;
原始查询
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
我的估计甚至更糟,只有一个估计行,而返回了1300。就像OP所述,我添加是否没关系OPTION(RECOMPILE)
需要注意的重要一点是,当我们摆脱where子句时,估计值是100%正确的,这是可以预期的,因为我们正在使用两个表中的所有数据。
我强迫索引只是为了确保我们使用与上一个查询相同的索引,以证明这一点
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
如预期的那样,很好的估计。
那么,我们可以改变什么以获得更好的估计,但仍在寻求我们的价值?
如果@UID是唯一的(如OP给出的示例),我们可以将id
返回的单个resources
变量放在变量中,然后使用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);
给出100%的准确估算
但是,如果资源中有多个resourceUID怎么办?
添加一些测试数据
INSERT INTO Resources(ResourceUID)
VALUES (38);
go 50
这可以用临时表解决
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
再次提供准确的估算。
这是用我自己的数据集YMMV完成的。
用sp_executesql编写
带有变量
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
带有临时表
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
我的测试仍然是100%正确的估计
select r.id, LEFT(remark, 512)
(或任何合理的子字符串长度)。