TSQL:如何优化这个更新查询
TSQL: How to optimize this update query
我一直在努力优化另一个程序员在我的工作中创建的过程,方法是重写整个查询以使用块语句而不是游标。但是我 运行 遇到了问题。我让我的过程 运行 一个小时,但它从未完成(而游标过程在 30 分钟内完成)。我开始调试,发现它永远不会通过以下查询:
-- Create our temp table
create table #tData(
First varchar(50) null,
Middle varchar(50) null,
Last varchar(50) null,
Address varchar(50) null,
Address2 varchar(50) null,
City varchar(30) null,
State varchar(2) null,
Zip varchar(20) null,
Phone varchar(20) null,
SSN varchar(20) null,
DOB varchar(15) null,
Gender varchar(1) null,
cID int null,
pNum varchar(50) null,
POFlag bit null,
MatchFound bit null,
Status varchar(10) null
)
-- Index the temp table
create clustered index IX_tData_Clust on #tData(First, Middle, Last, Address, SSN, DOB)
create index IX_tData on #tData(First, Middle, Last, Address, SSN, DOB)
-- Look for matches
update
#tData
set
#tData.MatchFound = 1,
#tData.cID = ClientDatabase.cID,
#tData.pNum = ClientDatabase.pNum
from
#tData,
ClientDatabase
where
#tData.Status = 'ACTIVE'
and (
(
#tData.SSN is not null
and ClientDatabase.SSN is not null
and #tData.SSN = ClientDatabase.SSN
)
or
(
#tData.First is not null
and ClientDatabase.FirstName is not null
and #tData.Last is not null
and ClientDatabase.LastName is not null
and #tData.Address is not null
and ClientDatabase.AddressLine1 is not null
and #tData.First = ClientDatabase.FirstName
and #tData.Last = ClientDatabase.LastName
and #tData.Address = ClientDatabase.AddressLine1
)
)
基本上,上述查询具有从文本文件批量插入的临时数据 table (#tData)。然后它试图在 ClientDatabase(有超过 500 万个条目)中找到匹配项。找到匹配项后,它会更新 table 行的 MatchFound、cID 和 pNum 单元格。我已经用小插入文件(5 行)对其进行了测试,它似乎完成得很好。当我进入数千个时,就像我们的大多数文件一样,它就会弹出。
我做了一些进一步的研究和调试,发现它当前 运行正在使用的机器在所有 6 个内核上都达到了最大值,它似乎只是在那个点之后挂起,直到程序被迫退出.
有没有人对我如何优化上述查询或者如何使 SQL 更好地管理查询有任何建议?非常感谢任何帮助。
我不太清楚你为什么要更新临时文件 (#temporary) table 但我会给出一些建议。
更新#temp table 对你来说可能不是一个好主意,因为你有数百万条记录要匹配,而#temp table 没有索引。
更好的方法是创建一个 table,上面有索引,然后将数据插入其中,然后将其与主 table 中的索引列匹配,这样您的查询将是显着更快。所以像这样
CREATE #TABLE(列 1 作为主键,列 2,....列)
插入#TABLE() 值()
现在您的临时 table 将一样快,您的查询也应该非常快。注意上面的代码是伪代码。
因为您的 WHERE
子句的第一部分是:
where
#tData.Status = 'ACTIVE'
-- ...
您需要在 Status
字段中输入 INDEX
。
其次,我会将 UPDATE
语句分成两个语句,每个 OR
部分一个。还要删除 NULL
检查,因为当您最终比较字段时,字段不能为空以使条件为真(除非您将 ANSI_NULLS
设置为 OFF
,但我对此表示怀疑) :
update
-- snipped for brevity
where
#tData.Status = 'ACTIVE'
and #tData.SSN = ClientDatabase.SSN
加上
update
-- snipped for brevity
where
#tData.Status = 'ACTIVE'
and #tData.First = ClientDatabase.FirstName
and #tData.Last = ClientDatabase.LastName
and #tData.Address = ClientDatabase.AddressLine1
第三,既然一切都清楚了,您会发现这些 UPDATE
语句使用新学校的 JOIN 语句更容易阅读(阅读 Bad habits to kick : using old-style JOINs)。
update
#tData
set
#tData.MatchFound = 1,
#tData.cID = ClientDatabase.cID,
#tData.pNum = ClientDatabase.pNum
from
#tData
INNER JOIN ClientDatabase ON
#tData.SSN = ClientDatabase.SSN
where
#tData.Status = 'ACTIVE'
加上
update
#tData
set
#tData.MatchFound = 1,
#tData.cID = ClientDatabase.cID,
#tData.pNum = ClientDatabase.pNum
from
#tData
INNER JOIN ClientDatabase ON
#tData.First = ClientDatabase.FirstName
and #tData.Last = ClientDatabase.LastName
and #tData.Address = ClientDatabase.AddressLine1
where
#tData.Status = 'ACTIVE'
第四:由于查找匹配项是在 table ClientDatabase
中,我认为您需要 ClientDatabase
中的索引。一种用于 SSN
查找,一种用于 FirstName + LastName + AddressLine1
查找。
检查 Actual Execution Plan 很可能会揭示这一点。如果此类索引不存在,可能会导致多次 table 扫描,这会破坏性能。
create nonclustered index IX_ClientDatabase_SSN on ClientDatabase(SSN);
create nonclustered index IX_ClientDatabase_Name_Address on ClientDatabase(FirstName,LastName,AddressLine1);
我一直在努力优化另一个程序员在我的工作中创建的过程,方法是重写整个查询以使用块语句而不是游标。但是我 运行 遇到了问题。我让我的过程 运行 一个小时,但它从未完成(而游标过程在 30 分钟内完成)。我开始调试,发现它永远不会通过以下查询:
-- Create our temp table
create table #tData(
First varchar(50) null,
Middle varchar(50) null,
Last varchar(50) null,
Address varchar(50) null,
Address2 varchar(50) null,
City varchar(30) null,
State varchar(2) null,
Zip varchar(20) null,
Phone varchar(20) null,
SSN varchar(20) null,
DOB varchar(15) null,
Gender varchar(1) null,
cID int null,
pNum varchar(50) null,
POFlag bit null,
MatchFound bit null,
Status varchar(10) null
)
-- Index the temp table
create clustered index IX_tData_Clust on #tData(First, Middle, Last, Address, SSN, DOB)
create index IX_tData on #tData(First, Middle, Last, Address, SSN, DOB)
-- Look for matches
update
#tData
set
#tData.MatchFound = 1,
#tData.cID = ClientDatabase.cID,
#tData.pNum = ClientDatabase.pNum
from
#tData,
ClientDatabase
where
#tData.Status = 'ACTIVE'
and (
(
#tData.SSN is not null
and ClientDatabase.SSN is not null
and #tData.SSN = ClientDatabase.SSN
)
or
(
#tData.First is not null
and ClientDatabase.FirstName is not null
and #tData.Last is not null
and ClientDatabase.LastName is not null
and #tData.Address is not null
and ClientDatabase.AddressLine1 is not null
and #tData.First = ClientDatabase.FirstName
and #tData.Last = ClientDatabase.LastName
and #tData.Address = ClientDatabase.AddressLine1
)
)
基本上,上述查询具有从文本文件批量插入的临时数据 table (#tData)。然后它试图在 ClientDatabase(有超过 500 万个条目)中找到匹配项。找到匹配项后,它会更新 table 行的 MatchFound、cID 和 pNum 单元格。我已经用小插入文件(5 行)对其进行了测试,它似乎完成得很好。当我进入数千个时,就像我们的大多数文件一样,它就会弹出。
我做了一些进一步的研究和调试,发现它当前 运行正在使用的机器在所有 6 个内核上都达到了最大值,它似乎只是在那个点之后挂起,直到程序被迫退出.
有没有人对我如何优化上述查询或者如何使 SQL 更好地管理查询有任何建议?非常感谢任何帮助。
我不太清楚你为什么要更新临时文件 (#temporary) table 但我会给出一些建议。
更新#temp table 对你来说可能不是一个好主意,因为你有数百万条记录要匹配,而#temp table 没有索引。
更好的方法是创建一个 table,上面有索引,然后将数据插入其中,然后将其与主 table 中的索引列匹配,这样您的查询将是显着更快。所以像这样
CREATE #TABLE(列 1 作为主键,列 2,....列) 插入#TABLE() 值()
现在您的临时 table 将一样快,您的查询也应该非常快。注意上面的代码是伪代码。
因为您的 WHERE
子句的第一部分是:
where
#tData.Status = 'ACTIVE'
-- ...
您需要在 Status
字段中输入 INDEX
。
其次,我会将 UPDATE
语句分成两个语句,每个 OR
部分一个。还要删除 NULL
检查,因为当您最终比较字段时,字段不能为空以使条件为真(除非您将 ANSI_NULLS
设置为 OFF
,但我对此表示怀疑) :
update
-- snipped for brevity
where
#tData.Status = 'ACTIVE'
and #tData.SSN = ClientDatabase.SSN
加上
update
-- snipped for brevity
where
#tData.Status = 'ACTIVE'
and #tData.First = ClientDatabase.FirstName
and #tData.Last = ClientDatabase.LastName
and #tData.Address = ClientDatabase.AddressLine1
第三,既然一切都清楚了,您会发现这些 UPDATE
语句使用新学校的 JOIN 语句更容易阅读(阅读 Bad habits to kick : using old-style JOINs)。
update
#tData
set
#tData.MatchFound = 1,
#tData.cID = ClientDatabase.cID,
#tData.pNum = ClientDatabase.pNum
from
#tData
INNER JOIN ClientDatabase ON
#tData.SSN = ClientDatabase.SSN
where
#tData.Status = 'ACTIVE'
加上
update
#tData
set
#tData.MatchFound = 1,
#tData.cID = ClientDatabase.cID,
#tData.pNum = ClientDatabase.pNum
from
#tData
INNER JOIN ClientDatabase ON
#tData.First = ClientDatabase.FirstName
and #tData.Last = ClientDatabase.LastName
and #tData.Address = ClientDatabase.AddressLine1
where
#tData.Status = 'ACTIVE'
第四:由于查找匹配项是在 table ClientDatabase
中,我认为您需要 ClientDatabase
中的索引。一种用于 SSN
查找,一种用于 FirstName + LastName + AddressLine1
查找。
检查 Actual Execution Plan 很可能会揭示这一点。如果此类索引不存在,可能会导致多次 table 扫描,这会破坏性能。
create nonclustered index IX_ClientDatabase_SSN on ClientDatabase(SSN);
create nonclustered index IX_ClientDatabase_Name_Address on ClientDatabase(FirstName,LastName,AddressLine1);