【问题标题】:T-SQL update top n rows for each group with n variable for each group (cross apply alternative)T-SQL 更新每个组的前 n 行,每个组有 n 个变量(交叉应用替代方案)
【发布时间】:2016-07-13 18:12:25
【问题描述】:

我有两个表 - SourceTable 和 DestTable - Source 包含列 RowNum 和 Col。我想加入 Col 上的两个表,然后更新 DestTable 的前“n”行,其中“n”是 SourceTable 中 RowNum 的值。我可以通过交叉应用来做到这一点,但这需要几个小时。我正在寻找另一种编写此查询的方式。

我的 Cross Apply 解决方案是 -

UPDATE t 
SET    dest = source 
FROM   #sourcetable s 
       CROSS apply (SELECT TOP(s.rownum) * 
                    FROM   #desttable d 
                    WHERE  d.col = s.col) t 

以下是样本数据-

源表-

Col, Source, RowNum    
11 , 111   , 2    
12 , 222   , 1

DestTable(更新前)-

Id, Col, Dest    
1 , 11 , 0
2 , 11 , 0
3 , 11 , 0    
4 , 12 , 0    
5 , 12 , 0

DestTable(更新后)-

Id, Col, Dest    
1, 11, 111    
2, 11, 111    
3, 11, 0    
4, 12, 222    
5, 12, 0

但这表现得非常糟糕(即使在正确索引的表上,当行数为数百万且列数接近 400 时也需要数小时)。

有没有更有效的方法来编写这个查询?

更新 - 实际表结构如下 -

DestTable(包含大约 100 万条记录,每列都已填充)- 38 个日期时间、66 个浮点数、31 个整数、210 个 varchar(50) 字段

Sourceable(它是在相关 sql 运行之前创建的临时表,包含大约 100k 条记录,并且每列都已填充) - 总共 16 个字段(4 个 int、2 个 datetime、1 个 float、rest varchar (50))

索引 - 我无法创建包含所有必需字段的覆盖索引,因为它们因查询而异。我尝试在各种字段组合上创建索引,发现在没有任何索引的情况下更新运行速度明显更快。

当前测试结果(行数在 DestTable 中)-

1000 行(没有任何索引)- 05:00 分钟

10000 行(没有任何索引)- 17:38 分钟

100000 行(没有任何索引)- 3 小时 7 分钟

100000(4 个字段上的任何索引)- 超过 7 小时我不得不停止它

【问题讨论】:

  • 我可以理解数百万行会使这变慢,但您需要解释多列如何影响这一点。此外,性能问题应包括EXPLAIN ANALYZE 以及有关表大小、索引、当前时间性能、期望时间等的一些信息。Slow 是一个相对术语,我们需要一个实际值来比较。
  • 谢谢,我添加了有关表结构和当前性能的信息。 100 万行所需的时间少于 20 分钟。我不确定解释分析是什么。它是一个标签还是应该添加到问题中的东西?
  • 将大更新拆分成小更新几乎总是明智之举,因为……嗯,大事务往往很慢。您是否尝试过使用光标,一次一个列?
  • 这是EXPLAIN PLAN ...顺便说一句,如果 1000 条记录是 5 分钟是非常合理的,说 100k 需要 3 小时。看起来时间增加与行数成线性关系。因此,如果您改进 1000 行的时间,其他的也会改进。
  • 知道了。我只更新一栏。让我问你一些别的问题 - 生成多个线程以同时更新不同的行是一个好主意吗(一个线程用于前一千行,另一个线程用于接下来的一千行等等)?我不知道数据库内部是如何工作的,不确定这是否会有所帮助或使事情变得更糟。

标签: sql sql-server tsql


【解决方案1】:

您可以尝试使用简单的JOIN 而不是相关子查询:

WITH cte AS
(
 SELECT d.id, d.col, d.dest, s.source
 FROM (SELECT *,
       rn = ROW_NUMBER() OVER(PARTITION BY col ORDER BY id) FROM #desttable) d
 JOIN #sourcetable s
   ON d.col = s.col
  AND d.rn <= s.rownum 
)
UPDATE cte
SET dest = source;

SELECT *
FROM #desttable;

LiveDemo


您应该发布您的真实数据样本、数据结构和查询计划。否则我们只能猜测如何改进它。

【讨论】:

  • @JuanCarlosOropeza :) 我用convertcsv.com/csv-to-sql.htm 来获取表格
  • 那不用选择其他的rdbms作为输出?我使用 sqlFiddle 中的 text to ddl 函数
  • @lad2025 谢谢,您的解决方案比交叉应用更快。执行时间从超过 7 小时减少到 4-6 小时。不过,我仍在尝试优化它。仅更新一百万条记录应该不会花费这么长时间。
  • @Achilles 代替UPDATE 试试CREATE TABLE dummyDest as SELECT ... 如果更快,你可以DROP TABLEALTER TABLE RENAME
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多