MSM*_*MSM 6 performance sql-server sql-server-2012 query-performance
我正在尝试优化以下查询(这是我能想到的最简化的版本):
SELECT tr.Id, StatusDate
FROM (
SELECT tr.Id, tr.StatusDate
FROM mon.ArchivedTaskResults_201504 as tr WITH (NOLOCK)
INNER JOIN mon.ViewDevicesWithGroups dev WITH (NOLOCK) ON tr.DeviceId = dev.Id
WHERE tr.ClientId = 4 AND dev.Deleted = 0
) AS tr
ORDER BY StatusDate DESC
OFFSET 1000000 rows
FETCH NEXT 25 ROWS ONLY
Run Code Online (Sandbox Code Playgroud)
问题是,查询性能与 OFFSET 成正比 - 对于 offset=0 查询在 0.0 秒内执行,但对于 offset=1000000 执行时间约为 23 秒(如果偏移量更大,则可能需要几分钟)。
我几乎可以肯定我的问题可以通过 ArchivedTaskResults 表上的适当聚集索引来解决,但是在尝试了几个小时之后我仍然没有找到好的索引。
ArchivedTaskResults 表真的很大,大约有 50000000 行(50 M)
附加信息:
如果有人能解决我上面描述的问题,我会非常高兴,但说实话,我的真实查询更加奇怪(免责声明:我不是设计这个数据库的人):
SELECT tr.Id, StatusDate
FROM (
(
SELECT tr.Id, StatusDate
FROM mon.TaskResults as tr WITH (NOLOCK)
INNER JOIN mon.ViewDevicesWithGroups dev WITH (NOLOCK) ON tr.DeviceId = dev.Id
WHERE tr.ClientId = 4 AND dev.Deleted = 0
) UNION ALL (
SELECT tr.Id, tr.StatusDate
FROM mon.ArchivedTaskResults_201504 as tr WITH (NOLOCK)
INNER JOIN mon.ViewDevicesWithGroups dev WITH (NOLOCK) ON tr.DeviceId = dev.Id
WHERE tr.ClientId = 4 AND dev.Deleted = 0
) UNION ALL (
SELECT tr.Id, tr.StatusDate
FROM mon.ArchivedTaskResults_201505 as tr WITH (NOLOCK)
INNER JOIN mon.ViewDevicesWithGroups dev WITH (NOLOCK) ON tr.DeviceId = dev.Id
WHERE tr.ClientId = 4 AND dev.Deleted = 0
)
) AS tr
ORDER BY StatusDate DESC
OFFSET 1000000 ROWS
FETCH NEXT 25 ROWS
Run Code Online (Sandbox Code Playgroud)
这要复杂得多,因为即使是 offset=0 的查询也有很长的执行时间(我猜是因为 sql server 不知道 ArchivedTaskResults_201505 出现在 ArchivedTaskResults_201504 之后,并试图在内存中对它们进行排序,但这只是我的盲注) .
如果有人设法帮助我进行该查询,那将超出我最疯狂的梦想,但如果由于奇怪的数据库设计而无法实现,我想我可以用软件解决它(我可以从我的应用程序中一次查询一个表,而不是在 SQL 中做所有事情。或者为此使用存储过程)-但前提是我的第一个查询可以改进。
提前致谢。
我认为您在使用时无法获得良好的性能OFFSET
。数据库必须搜索内部查询的 1,000,025 行输出;即使您在TaskResults
系统上有一个良好的聚集索引,也不确定它是否可以跳到日期 X。
但你做了!假设这是针对某种 GUI,请记下上StatusDate
一个查询中最早的查询,然后使用它来调整下一页:
SELECT
tr.Id, StatusDate
FROM
(
SELECT tr.Id, tr.StatusDate
FROM mon.ArchivedTaskResults_201504 as tr WITH (NOLOCK)
INNER JOIN mon.ViewDevicesWithGroups dev WITH (NOLOCK) ON tr.DeviceId = dev.Id
WHERE tr.ClientId = 4 AND dev.Deleted = 0
AND
(
-- Retrieve only records from before the previous page
tr.StatusDate < @PrevStatusDate
OR (tr.StatusDate = @PrevStatusDate AND tr.Id < @PrevID)
)
) AS tr
ORDER BY StatusDate, Id DESC
FETCH NEXT 25 ROWS ONLY
Run Code Online (Sandbox Code Playgroud)
因此,如果第 #123 页以 2015/05/01 结束,记录 #234,则您需要考虑 2015/04/30 或更早的所有记录,或者同样来自 2015/05/01 但属于记录 #1 的所有记录..#233。
这应该适用于更复杂的 UNION 查询,但“真正的”分区可能比这种滚动分区更容易。
如果StatusDate
是唯一的,或者偶尔在两个相邻页面上显示相同的记录是可以接受的,则可以删除 和@PrevID
位ORDER BY Id
。如果Id
总是增加,您可以过滤掉它并跳过StatusDate
。
请记住,如果在基础数据中添加、删除或重新排序记录,则像这样检索页面可以轻松地跳过一条记录或包含同一记录两次。但这是另一个话题了。
归档时间: |
|
查看次数: |
3736 次 |
最近记录: |