要求是在分页中加载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
它提高了性能,但达不到目标。
答案 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的观点,我们没有足够的信息来解决整个问题的完整分析。)