SQL Merge - 我该如何优化它?
Posted
技术标签:
【中文标题】SQL Merge - 我该如何优化它?【英文标题】:SQL Merge - How can I optimize this? 【发布时间】:2013-12-12 21:47:56 【问题描述】:表 A(要合并的表)有 90,000 行 表 B(源表)有 3,677 行 我希望这会很快合并,但需要 30 分钟(并且还在计数)。 如何优化它以更快地运行?
ALTER PROCEDURE [dbo].[MergeAddressFromGraph]
-- no params
作为 开始 -- 添加了 SET NOCOUNT ON 以防止额外的结果集 -- 干扰 SELECT 语句。 设置无计数;
-- first add fids to the MergeFundraiserNameAddress table instead of the temp table?
SELECT fundraiserid, ein
INTO #fids
FROM bb02_fundraiser
BEGIN TRAN;
MERGE BB02_FundraiserNameAddress AS T
USING
(
select f.fundraiserid,
n.addresslines,
n.town,
n.county,
n.postcode,
n.country,
n.fulladdress,
n.ein
from MergeFundraiserNameAddress n
join bb02_fundraiser f
on f.ein = n.ein and f.isdefault = 1
group by n.ein,
f.fundraiserid,
n.addresslines,
n.town,
n.county,
n.postcode,
n.country,
n.fulladdress
) AS S
ON (T.fundraiserid in( (select fundraiserid from #fids where ein = S.ein)) )
WHEN MATCHED
THEN UPDATE
SET
-- ADDRESS
T.addresslines = S.addresslines
,T.town = S.town
,T.county = S.county
,T.postcode = S.postcode
,T.country = S.country
,T.fulladdress = S.fulladdress
;
DELETE FROM MergeFundraiserNameAddress
COMMIT TRAN;
drop table #fids
结束
更新 我能够改进现在只需几秒钟即可运行的存储过程。我加入了 temp 表而不是 bb02_fundraiser 表,并删除了 ON 子句中的子查询。
我现在意识到 Merge 不是必需的,我可以使用 Update 代替,但我现在可以接受,因为重构中可能很快需要 INSERT。
下面更新了存储过程 如果 OBJECT_ID('tempdb..#fids') 不为空 删除表#fids
SELECT fundraiserid, ein
INTO #fids
FROM bb02_fundraiser
where isdefault = 1
BEGIN TRAN;
MERGE BB02_FundraiserNameAddress AS T
USING
(
select f.fundraiserid,
n.addresslines,
n.town,
n.county,
n.postcode,
n.country,
n.fulladdress,
n.ein
from MergeFundraiserNameAddress n
join #fids f
on f.ein = n.ein
group by n.ein,
f.fundraiserid,
n.addresslines,
n.town,
n.county,
n.postcode,
n.country,
n.fulladdress
) AS S
ON (T.fundraiserid = S.fundraiserid)
WHEN MATCHED
THEN UPDATE
SET
-- ADDRESS
T.addresslines = S.addresslines
,T.town = S.town
,T.county = S.county
,T.postcode = S.postcode
,T.country = S.country
,T.fulladdress = S.fulladdress
;
DELETE FROM MergeFundraiserNameAddress
COMMIT TRAN;
IF OBJECT_ID('tempdb..#fids') IS NOT NULL
DROP TABLE #fids
【问题讨论】:
请发布执行计划。你是对的 - 我们可以很快做到这一点。 如果唯一可能的结果是更新,为什么要使用 MERGE? (Here's why you might want to use caution with MERGE.) 另外,也许它被阻止了。当它运行 30 分钟时,sys.dm_exec_requests 会说什么?此 DMV 可以向您显示 blocking_session_id 以及试图运行 MERGE 的 session_id 的等待类型。 【参考方案1】:如果仅此声明就可以完成这项工作,请参见下文。
UPDATE T
SET T.addresslines = n.addresslines
,T.town = n.town
,T.county = n.county
,T.postcode = n.postcode
,T.country = n.country
,T.fulladdress = n.fulladdress
from MergeFundraiserNameAddress n join bb02_fundraiser f
on f.ein = n.ein and f.isdefault = 1
INNER JOIN BB02_FundraiserNameAddress T
ON T.fundraiserid = f.fundraiserid AND T.ein = f.ein
group by n.ein,
f.fundraiserid,
n.addresslines,
n.town,
n.county,
n.postcode,
n.country,
n.fulladdress
正如其他用户在您的 cmets 中提到的,为什么在您只更新记录时使用 MERGE 语句。 MERGE 语句用于执行多个操作,例如 UPDATE 、 DELETE 和 INSERT。
由于您只是更新记录,因此不需要合并语句。
性能缓慢的原因
由于您正在获取 Temp 表中的所有记录,然后将其与其他表连接,并且没有在该 Temp 表上创建任何索引,因此缺少任何索引会损害查询性能。
当您执行 SELECT * INTO #TempTable FROM Some_Table 时,它会将 Some_Table 中的所有数据带入 Temp 表,但不会带入索引。你可以通过运行这个简单的查询来看到你自己
select * from tempdb.sys.indexes
where object_id = (select object_id
from tempdb.sys.objects
where name LIKE '#TempTable%')
【讨论】:
不错的建议,但 3,000 行表上的索引不会有任何区别 @ElectricLlama 是的,但如果 OP 抱怨性能,第一个嫌疑人将是他代码中的 Temp 表。 我不同意,我们有一个从 3,677 行表更新的 90,000 行表,我怀疑是 90,000 行表。通过观察查询计划和 I/O 等,怀疑可以变成确定性。 @ElectricLlama 我同意你的观点,但为什么你坚持认为用户的临时表中有大约 3000 行。显然,在查询缓慢的情况下,任何人都会在创建索引或任何东西之前看到执行计划,但在这种特殊情况下,当没有提供执行计划时,我只是在猜测,而且我的猜测和你的猜测一样好,因为你和我都没有此查询的任意数量的行或执行计划:) 他在他的 cmets 中提到了行数。当然,行数可能会改变,在这种情况下索引绝对值得。【参考方案2】:还有为什么可以截断时删除。
截断表 MergeFundraiserNameAddress
【讨论】:
我不是 SQL 专家,所以还有很多事情我不知道。谢谢你的建议。我查找了截断与删除,看看你的意思。 ***.com/questions/3256242/…以上是关于SQL Merge - 我该如何优化它?的主要内容,如果未能解决你的问题,请参考以下文章