放入指数后,读数并未降低

时间:2013-08-15 17:18:40

标签: sql-server sql-server-2008 indexing non-clustered-index

要求是在分页中加载50条记录,其中所有65列表“empl”的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建议我为第一个查询包含所有列的索引,因此我更改了建议的索引,将其转换为基本四个过滤器的过滤器索引,以使其更有效。

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

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

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

第四次编辑:

现在我已经找到了一个改善了性能的解决方案,但仍未实现目标。关键是它不会用于所有数据

查询如下:

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

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

2 个答案:

答案 0 :(得分:0)

您正在选择e.uon desc排序的前50行。以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

答案 1 :(得分:0)

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

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

下一个皱纹:您不希望直接跨列,您希望通过相当多种类型的过滤器过滤列。聚集索引可能会拉出前50列,但WHERE可以过滤掉任何,部分或全部。为了“填补你的跨度”,几乎肯定要阅读更多内容。

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

所以:正如我所看到的那样(即我没有在本地测试它),SQL必须首先组装数据,过滤掉无效行(涉及表连接),排序剩余的内容,然后返回该跨度您感兴趣的行。我相信,无论是否使用uon上的索引,SQL都会确定需要读取所有数据并过滤/排序才能选择所需的范围。

(你的新索引似乎不够。第六列是sid,但查询中没有引用sid,所以它可能只能帮助“到目前为止”。这引发了很多关于数据基数和什么,在这一点上,我推迟到@Aarons的观点,我们没有足够的信息来解决整个问题的完整分析。)