1

要求是在分页中加载 50 条记录,表“empl”的所有 65 列以最小 IO 加载。表中有 280000+ 条记录。PK 上只有一个聚集索引。

分页查询如下:

WITH result_set AS (    
SELECT    
ROW_NUMBER() OVER (ORDER BY  e.[uon] DESC ) AS [row_number], e.*    
FROM    
empl e with (NOLOCK)    
LEFT JOIN empl_add ea with (NOLOCK)    
ON ea.ptid = e.ptid    
WHERE    
e.del = 0 AND e.pub = 1 AND e.sid = 2
AND e.md = 0     
AND e.tid = 3    
AND e.coid = 2     
AND (e.cid = 102)
AND ea.ptgid IN (SELECT ptgid FROM empl_dep where psid = 1001
AND ib = 1)) 
SELECT  
*  
FROM  
result_set  
WHERE  
[row_number] BETWEEN 0 AND 50

以下是从探查器运行上述查询后的统计信息:

CPU:1500,读取:25576,持续时间:25704

然后我将以下索引放在表 empl 上:

CREATE NONCLUSTERED INDEX [ci_empl]
ON [dbo].[empl] ([del],[md],[pub],[tid],[coid],[sid],[ptid],[cid],[uon])
GO

放入索引后 CPU 和 Reads 仍然更高。我不知道索引有什么问题或查询有什么问题?

编辑:

以下查询在放入索引后也进行了高读取。而且只有 3 列和 1 个计数。

SELECT TOP (2147483647)
ame.aid ID, ame.name name,         
COUNT(empl.pid) [Count], ps.uff uff FROM ame with (NOLOCK)        
JOIN pam AS pa WITH (NOLOCK) ON pa.aid = ame.aid         
JOIN empl WITH (NOLOCK) ON empl.pid = pa.pid         
LEFT JOIN psam AS ps
ON ps.psid = 1001
AND ps.aid = ame.aid
LEFT JOIN empl_add ea with (NOLOCK)        
ON ea.ptid = empl.ptid        
WHERE 
empl.del = 0 AND empl.pub = 1 AND empl.sid = 2
AND empl.md = 0         
AND (empl.tid = 3)        
AND (empl.coid = 2)        
AND (empl.cid = 102)        
AND ea.ptgid IN (SELECT ptgid FROM empl_dep where psid = 1001
AND ib = 1)        
AND ame.pub = 1 AND ame.del = 0        
GROUP BY ame.aid, ame.name, ps.uff        
ORDER BY ame.name ASC

第二次编辑:

现在我在“uon”列上放置了以下索引:

CREATE NONCLUSTERED INDEX [ci_empl_uon]
ON [dbo].[empl] (uon)
GO

但是 CPU 和读取仍然更高。

第三次编辑:

DTA 建议我对第一个查询中包含的所有列进行索引,因此我更改了建议的索引,将其转换为基本四个过滤器的过滤器索引,以使其更有效。

我在创建索引时在Include之后添加了以下行。

Where e.del = 0 AND e.pub = 1 AND e.sid = 2 AND e.md = 0 AND e.coid = 2

但是在开发和生产机器上的读取仍然很高。

第四次编辑:

现在我找到了一个提高性能的解决方案,但仍然达不到目标。关键是它不适用于ALL THE DATA

查询如下:

WITH result_set AS (    
SELECT    
ROW_NUMBER() OVER (ORDER BY  e.[uon] DESC ) AS [row_number], e.pID pID   
FROM    
empl e with (NOLOCK)    
LEFT JOIN empl_add ea with (NOLOCK)    
ON ea.ptid = e.ptid    
WHERE    
e.del = 0 AND e.pub = 1 AND e.sid = 2
AND e.md = 0     
AND e.tid = 3    
AND e.coid = 2     
AND (e.cid = 102)
AND ea.ptgid IN (SELECT ptgid FROM empl_dep where psid = 1001
AND ib = 1)) 
SELECT  
*  
FROM  
result_set join empl on result_set.pID = empl.pID
WHERE  
[row_number] BETWEEN @start AND @end

并使用键列更改、包含和过滤重新创建索引:

CREATE NONCLUSTERED INDEX [ci_empl]
ON [dbo].[empl] ([ptid],[cid],[tid],[uon])
INCLUDE ([pID])
Where 
[coID] = 2 and
[sID] = 2 and
[pub] = 1 and
[del] = 0 and
[md] = 0
GO

它提高了性能,但达不到目标。

4

2 回答 2

0

您正在选择按 排序的前 50 行e.uon desc。以 开头的索引uon将加快查询速度:

create index IX_Empl_Uon on dbo.empl (uon)

该索引将允许 SQL Server 扫描此索引的前 N ​​行。N 是分页中的最大数字:对于 50 个元素的第 3 页,N 等于 150。SQL Server 然后执行 50 次键查找以从聚集索引中检索完整的行。据我所知,这是一个教科书示例,说明索引可以产生很大的不同。

row_number() over ... as rn并不是所有的查询优化器都会足够聪明地注意到where rn between 1 and 50前 50 行。但 SQL Server 2012 可以。它对第一页和连续页都使用索引,例如row_number() between 50 and 99.

于 2013-08-15T17:33:58.393 回答
0

您正在尝试根据 uon 列指定的顺序从数据集中查找 X 到 X+N 行。

我在这里假设 uon 是提到的主键。如果不是,没有索引,其中 uon 是第一列(如果不是唯一的),则表扫描是不可避免的。

下一个问题:您不希望列的直接跨度,您希望列的跨度被相当广泛的过滤器过滤。聚集索引可能会提取前 50 列,但 WHERE 可能会过滤掉任何、部分或全部。为了“填满你的跨度”,几乎肯定需要阅读更多内容。

更有趣:您对表 empl_add 执行左外连接(例如,即使没有找到 empl_add,也保留 empl 行),然后要求过滤掉子查询中未找到 empladd.ptgid 的所有行。不妨把它做成一个内部连接,它可能会加快速度,当然不会让它们变慢。它也是一个“过滤因素”,不能用表 empl 上的索引来解决。

所以:正如我所见(即我没有在本地测试它),SQL 必须首先组装数据,过滤掉无效行(涉及表连接),排序剩余的行,然后返回你感兴趣。我相信,无论有没有 uon 上的索引,SQL 都在确定需要读取所有数据并进行过滤/排序,然后才能挑选出所需的范围。

(您的新索引似乎不够用。第六列是 sid,但查询中未引用 sid,因此它可能只能“到目前为止”提供帮助。这引发了很多关于数据基数和诸如此类的问题,在哪一点我遵循@Aarons 的观点,即我们对整个问题集的信息不足,无法进行全面分析。)

于 2013-08-15T18:47:36.787 回答