带有相关子查询的 While 循环的 SQL Server 性能调整
Posted
技术标签:
【中文标题】带有相关子查询的 While 循环的 SQL Server 性能调整【英文标题】:SQL Server Performance tuning of While loop with correlated sub query 【发布时间】:2013-01-22 14:07:22 【问题描述】:在我的项目中,我遇到了以下 T-SQL 代码的挑战。
-
step1 使用父模块及其订阅用户填充 UserModules 表
step2 在 Modules_Hierarchy 表中检查与 step1 中的模块关联的子模块,并通过将子模块与父模块订阅的用户映射,将有效记录插入到 UserModules 表中。
此步骤将递归重复,直到找到所有子模块。
问题:
在第 2 步中,WHILE 循环和 SELECT 语句使用相关子查询,并且表 UserModules 是 INSERT 和相关 SELECT 子句的一部分,这会妨碍性能,并且查询经常会因以下 LOCK 升级问题而失败。
ModulesUsers 表中的最终数据大小为 4200 万,预计还会增长。
错误消息:“SQL Server 数据库引擎的实例此时无法获取 LOCK 资源。当活动用户较少时重新运行您的语句。请数据库管理员检查此实例的锁和内存配置,或检查长时间运行的事务。”
如何优化此查询,即第 2 步来解决问题?
第一步:
INSERT INTO UserModules(ModuleID, UserID)
SELECT ModuleID, UserID
FROM TABLEA a
INNER JOIN TABLEB b ON a.ID = b.ID
第二步:
DECLARE @cnt int
SET @cnt = 1
WHILE( @cnt > 0 )
BEGIN
SET @cnt = (SELECT COUNT(DISTINCT s.moduleid)
FROM Modules_Hirarchy s WITH (nolock), Modules t
WHERE s.ParentModuleId = t.ModuleId
------------
AND NOT EXISTS
(SELECT ModuleId + EndUserId
FROM UserModules r
WHERE s.moduleid = r.moduleid
AND t.EndUserId = r.EndUserId)
AND s.moduleid + t.EndUserId NOT IN
(SELECT CAST(ModuleId AS varchar) + EndUserId
FROM UserModules ))
IF @cnt = 0
BREAK
INSERT INTO UserModules (ModuleId, EndUserId)
SELECT DISTINCT s.moduleid, t.EndUserId
FROM Modules_Hirarchy s WITH (nolock), UserModules t
WHERE s.ParentModuleId = t.ModuleId
AND NOT EXISTS
(SELECT ModuleId + EndUserId
FROM UserModules r
WHERE s.moduleid = r.moduleid
AND t.EndUserId = r.EndUserId)
END
【问题讨论】:
任何合理的现代 SQL Server 版本 (>= 2005) 都有 CTEs,旨在执行这种递归,而无需编写显式循环代码。 请发布一些示例数据 【参考方案1】:一些数据可供使用
create table #UserModules(ModuleID int, UserID int)
create table #Modules_Hirarchy(ParentModuleID int, ChildModuleID int)
insert into #UserModules (ModuleID , UserID)
values(1,1)
,(2,1)
,(3,1)
,(4,1)
,(5,1)
,(6,2)
,(7,2)
insert into #Modules_Hirarchy(ParentModuleID , ChildModuleID )
values (null,1)
,(1,2)
,(2,3)
,(3,4)
,(3,5)
,(null,6)
,(6,7)
分辨率
with cts(ModuleID, UserID,parentModule ) as
(
select a.ModuleID, a.UserID , CAST(null as int)as parentModule --, cAST(null as int)as b
from #UserModules a join #Modules_Hirarchy b on a.ModuleID = b.ChildModuleID
where b.ParentModuleID is null
union all
select b.ChildModuleID as ModuleID, a.UserID, b.ParentModuleID
from cts a join #Modules_Hirarchy b
on a.ModuleID = b.ParentModuleID
)
select *
into #RESULT
from cts
编辑 很难说:)对许多变量 但是你应该做些什么来提高查询效率
在ModuleID ParentModuleID ChildModuleID
列上单独的非聚集索引
您可能不想查询所有组,而只想查询一个 显式过滤出尽可能多的组 声明
选择 a.ModuleID、a.UserID、CAST(null as int)作为 parentModule 从 #UserModules a 加入 #Modules_Hirarchy b on a.ModuleID = b.ChildModuleID 其中 b.ParentModuleID 为 null,a.ModuleId 在 (listOfModules)
为列 (ParentModuleID, ChildModuleID)
添加唯一索引,因为非唯一行可能会导致大量行重复
除了它依赖于 ParentModuleID ChildModuleID 上的数据选择性,但你无能为力
我认为它适用于大数据集,因为谓词很简单,只要数据选择性很高
【讨论】:
我有一个问题,这个解决方案可以被称为巨大的记录,即 40 多百万条记录。 感谢您的建议。我在我的项目中尝试了这个解决方案,它工作得很好,但有一个例外,即有时它会因“MyDatabase”上的错误事务日志已满而失败。请建议我解决此问题的方法。 呃,我真的不知道,我认为你应该将整个交易作为新问题发布,发布信息你的日志文件有多大等等等等以上是关于带有相关子查询的 While 循环的 SQL Server 性能调整的主要内容,如果未能解决你的问题,请参考以下文章