The*_*Man 5 sql-server paging performance fetch
我有一个相当复杂的 SQL 查询,涉及从大量联接返回大约 20 列,用于填充 UI 中的结果网格。它还使用几个 CTE 来预过滤结果。我已经包含了下面查询的近似值(我已经注释掉了修复性能的行)
随着数据库中数据量的增加,查询性能大幅下降,主表“Contract”中只有大约 2500 行。
通过实验,我发现仅通过删除最后的顺序和偏移获取,性能就从大约 30 秒缩短到仅 1 秒!
order by 1 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
Run Code Online (Sandbox Code Playgroud)
这对我来说毫无意义。最后一行应该非常便宜,即使 OFFSET 为零也是免费的,那么为什么它会增加我的查询时间 29 秒呢?
为了保持 SQL 的相同功能,我对其进行了调整,以便首先选择 #TEMP,然后对临时表执行上述 order-offset-fetch,然后删除临时表。这将在大约 2-3 秒内完成。
我的“优化”感觉很错误,肯定有更明智的方法来达到相同的速度吗?
我还没有针对更大的数据集对此进行广泛测试,这本质上是目前恢复性能的快速修复。我怀疑随着数据大小的增长它是否会有效。
除了主键上的聚集索引之外,表上没有任何索引。查询执行计划似乎没有显示任何主要瓶颈,但我不是解释它的专家。
WITH tableOfAllContractIdsThatMatchRequiredStatus(contractId)
AS (
SELECT DISTINCT c.id
FROM contract c
INNER JOIN site s ON s.ContractId = c.id
INNER JOIN SiteSupply ss ON ss.SiteId = s.id AND ss.status != 'Draft'
WHERE
ISNULL(s.Deleted, '0') = 0
AND ss.status in ('saved')
)
,tableOfAllStatusesForAContract(contractId, status)
AS (
SELECT DISTINCT c.id, ss.status
FROM contract c
INNER JOIN site s ON s.ContractId = c.id
INNER JOIN SiteSupply ss ON ss.SiteId = s.id AND ss.status != 'Draft'
WHERE ss.SupplyType IN ('Electricity') AND ISNULL(s.Deleted, '0') = 0
)
SELECT
[Contract].[Id]
,[Contract].[IsMultiSite]
,statuses.StatusesAsCsv
... lots more columns
,[WaterSupply].[Status] AS ws
--INTO #temp
FROM
(
SELECT
tableOfAllStatusesForAContract.contractId,
string_agg(status, ', ') AS StatusesAsCsv
FROM
tableOfAllStatusesForAContract
GROUP BY
tableOfAllStatusesForAContract.contractId
) statuses
JOIN contract ON Contract.id = statuses.contractId
JOIN tableOfAllContractIdsThatMatchRequiredStatus ON tableOfAllContractIdsThatMatchRequiredStatus.contractId = Contract.id
JOIN Site ON contract.Id = site.contractId and site.isprimarySite = 1 AND ISNULL(Site.Deleted,0) = 0
... several more joins
JOIN [User] ON [Contract].ownerUserId = [User].Id
WHERE isnull(Deleted, 0) = 0
AND
(
[Contract].[Id] = '12659'
OR [Site].[Id] = '12659'
... often more search term type predicates here
)
--select * from #temp
order by 1
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
--drop table #temp
Run Code Online (Sandbox Code Playgroud)
我还没有答案,所以我将尝试自己解释它,承认我对 SQL 工作原理的理解很差,并且上面的评论中来自 Jeroen 的一些指示。这可能不正确,但从我发现的情况来看,它可能是正确的,而且我确实知道如何解决我眼前的问题,以便它可以帮助其他人。
我将用一个类比来解释它,因为我认为这可能会发生:
想象一下,您是一家餐厅的厨师,您必须准备大量饭菜 ( rows in results)。您知道将会有很多事情,因为您的前台已告诉您这一点 ( TOP 10 or FETCH 10)。
您花时间列出所需的多种原料 ( table joins) 和您需要的设备,当第一个订单收到时,您确保自己的效率非常高。将第一个订单所需的部分切碎,放入小碗中,以备后续订单使用。第一个订单需要花费相当长的时间 ( 30 secs),因为您正在提前计划并希望后续菜肴尽快上桌。
然而,当你坐在厨房里等待下一个订单时……那就不要来了。就这样,只需一个命令。好吧,那是浪费时间!如果你只是想把一道菜拿出来,你本来可以做得更快(1sec),但你正在提前计划一些从来不需要的东西。
第二天晚上,你放弃了之前的策略,只一次做每个盘子。然而这次,有数百名顾客。你无法以足够快的速度一次交付一个。如果您像前一天晚上那样提前计划,那么交付所有订单的时间会快得多。(我还没有测试这个假设,但我预计这可能会发生)。
对于我的查询,我不知道是否会有 1 个结果或 100 个结果,尽管我可以根据用户输入的搜索条件预先进行一些分析,但我可能必须调整我的 UI 以提供更多结果信息,以便我可以更好地预测这一点,这意味着我可以为 SQL 选择合适的策略来预先使用。事实上,我针对少量结果进行了优化,目前效果很好 - 但我需要进行一些更广泛的测试,以了解随着数据集的增长性能如何受到影响。
"If you want a answer to something, post something that's wrong on the internet and someone will be sure to correct you"
| 归档时间: |
|
| 查看次数: |
7625 次 |
| 最近记录: |