如何加快 SQL 中的连接更新?我的陈述似乎无限期地运行

Posted

技术标签:

【中文标题】如何加快 SQL 中的连接更新?我的陈述似乎无限期地运行【英文标题】:How can I speed up a joined update in SQL? My statement seems to run indefinitely 【发布时间】:2008-11-24 14:38:53 【问题描述】:

我有两个表:一个源表和一个目标表。目标表将具有源表列的子集。我需要通过基于另一列加入源表来更新目标表中的单个列。更新语句如下:

UPDATE target_table tt
SET special_id = ( SELECT source_special_id
                   FROM source_table st
                   WHERE tt.another_id = st.another_id )

由于某种原因,此语句似乎无限期地运行。内部选择在自行执行时几乎立即发生。该表大约有 50,000 条记录,并且托管在一台功能强大的机器上(资源不是问题)。

我这样做正确吗?上述任何原因不能及时奏效?有更好的方法吗?

【问题讨论】:

另一张表的大小如何?无限期有多长?强大是什么意思? 无限期 == 天。源表有 120 万条记录,目标表有 50,000 条记录。 【参考方案1】:

您的初始查询对外部表中的每一行执行一次内部子查询。看看 Oracle 是否更喜欢这个:

UPDATE target_table 
SET special_id = st.source_special_id
FROM 
    target_table tt
    INNER JOIN
    source_table st
        WHERE tt.another_id = st.another_id 

(在更正发布的查询后编辑)

添加: 如果连接语法在 Oracle 上不起作用,该怎么办:

UPDATE target_table 
SET special_id = st.source_special_id
FROM 
    target_table tt, source_table st
WHERE tt.another_id = st.another_id 

重点是连接两个表,而不是使用您当前使用的外部查询语法。

【讨论】:

我收到一条未正确结束的 SQL 命令。添加一个';'最后没有解决它。 我已经有几年没用过Oracle了,但是对于SQL Server(我相信标准的ANSI SQL)你应该在UPDATE子句中使用表别名。否则,如果您执行了自加入,更新将是不明确的。 这不是有效的 Oracle 语法 @Tom,不,这对于 SQL 是正确的,别名必须在 from 子句中。 @Tony for Oracle,我不确定,但您可能必须在 FROM 之后使用 WHERE 子句来连接较旧的 ANSI。 服务器如何知道要在此处更新什么: UPDATE MyTable SET MyInt = t1.MyInt + 1 FROM MyTable t1 INNER JOIN MyTable t2 ON t2.ParentID = t1.ID 而以下内容很清楚: UPDATE t2 SET MyInt = t1.MyInt + 1 FROM MyTable t1 INNER JOIN MyTable t2 ON t2.ParentID = t1.ID【参考方案2】:

source_table(another_id) 上是否有索引?如果不是,source_table 将对 target_table 中的每一行进行一次完全扫描。如果 target_table 很大,这将需要一些时间。

source_table 中的某些 target_table 行是否可能不匹配?如果是这样,您的更新会将这些行的 special_id 设置为 null。如果您想避免这种情况,请执行以下操作:

UPDATE target_table tt
SET special_id = ( SELECT source_special_id
                   FROM source_table st
                   WHERE tt.another_id = st.another_id )
WHERE EXISTS( SELECT NULL
              FROM source_table st
              WHERE tt.another_id = st.another_id );

如果 target_table.another_id 被声明为引用 source_table.another_id 的外键(在这种情况下不太可能),这将起作用:

UPDATE
  ( SELECT tt.primary_key, tt.special_id, st.source_special_id
    FROM   tatget_table tt
    JOIN   source_table st ON st.another_id = tt.another_id
  )
SET special_id = source_special_id;

【讨论】:

【参考方案3】:

你确定它正在运行吗?

您是否寻找过阻塞锁?无限期是很长的时间,通常只能通过停止执行来实现。

【讨论】:

【参考方案4】:

更新:好的,现在查询已经修复了——我已经做了很多次了,在超过 50K 行的未索引表上,它在 Oracle 10g 和 9i 中运行良好.所以这里发生了其他事情;是的,您正在调用嵌套循环,但不,它不应该永远运行,即便如此。这些表的主键是什么?您是否有第二个表中的多行与第一个表的同一行匹配?您可能会尝试一遍又一遍地重写整个表,从而使锁定系统陷入困境。


原始回复

该语句实际上没有意义——您是在告诉它将所有 id 匹配的行更新为相同的 id(意思是,不会发生任何变化!)。

我想真实的说法看起来有点不同?

还请提供表架构信息(2 个表的主键、任何可用索引等)。

【讨论】:

我认为重点是子查询...不过,您在 ID 更新上是对的,但这没有意义。但我认为他的语句对每个路由器行执行一次内部查询。 哎呀,我打错了上面的语句。我修好了它。加入发生在不同的 ID 上。【参考方案5】:

不确定 Oracle 有什么可用的,但 MS Sql Server 有一个调优顾问,您可以将查询输入它,它会给出添加索引等的建议...我假设 Oracle 有类似的东西。

这将是查明问题的最快方法。

【讨论】:

【参考方案6】:

我不了解 Oracle,但 MSSQLServer 优化器可以为您将子查询转换为联接。

听起来您可能正在对短期或新创建的表进行数据导入。很容易忽略索引这些类型的表。我会确保 sourcetable.anotherid 上有一个索引 - 或者 sourcetable.anotherid、sourcetable.specialid 上有一个覆盖索引(顺序很重要,anotherid 应该放在第一位)。

在这些情况下(查询意外运行超过 1 秒)。最好弄清楚您的环境如何提供查询计划。检查该计划,问题就会变得清晰。

你看,没有“优化的 sql 代码”这样的东西。 Sql 代码永远不会执行 - 从代码生成查询计划,然后执行这些计划。

【讨论】:

与任何其他 >2GL 语言有什么不同吗?今天编写的大多数代码都没有执行……它变成了一系列可以执行的指令。你是说我不能编写优化的 C++ 或 VB 或 Java? SQL->查询计划的转换程度比代码的编译要严重一些。我们谈论的是影响大 O 的决策。这就是为什么相同的代码可以在 5 年或 5 毫秒内运行的原因。【参考方案7】:

检查表上的统计信息是否是最新的 - see this question

【讨论】:

【参考方案8】:

在 Oracle 11g 中尝试 Codewerks 的回答时,我遇到了同样的问题,并且得到了“SQL 命令未正确结束”。谷歌搜索了一下Oracle MERGE statement,我将其改编如下:

MERGE INTO target_table tt
USING source_table st
ON (tt.another_id = st.another_id)
WHEN MATCHED THEN
  UPDATE SET tt.special_id = st.special_id;

如果您不确定another_id 的所有值都在source_table 中,那么您可以使用WHEN NOT MATCHED THEN 子句来处理这种情况。

【讨论】:

【参考方案9】:

如果您在加入“another_id”时有约束让 Oracle 知道存在 1-1 关系,我认为这可能会很好:

UPDATE ( SELECT  tt.rowid tt_rowid, tt.another_id, tt.special_id, st.source_special_id
         FROM    target_table tt, source_table st
         WHERE   tt.another_id = st.other_id
         ORDER BY tt.rowid )                   
SET special_id = source_special_id

当您使用连接更新和可更新视图时,按ROWID 排序很重要。

【讨论】:

以上是关于如何加快 SQL 中的连接更新?我的陈述似乎无限期地运行的主要内容,如果未能解决你的问题,请参考以下文章

如何提高我的查询性能 SQL Server

如何加快我在 sql 中的查询

数据库连接 MySQL 或 Berkeley DB

加快sql,加入两列

我的Java SQL Update语句没有更新我的数据库,我不知道它是否与语句或连接有关

如何加快 Spark 中的大数据框连接