Oracle SQL:使用另一个表中的数据更新表


251

表格1:

id    name    desc
-----------------------
1     a       abc
2     b       def
3     c       adf

表2:

id    name    desc
-----------------------
1     x       123
2     y       345

在oracle SQL中,如何运行sql更新查询,该查询可以使用表2 namedesc使用表2来更新表1 id?所以我得到的最终结果是

表格1:

id    name    desc
-----------------------
1     x       123
2     y       345
3     c       adf

问题取自用另一个表中的数据更新一个表,但专门针对oracle SQL。



您需要回到另一个问题,不接受该答案,并特别声明您需要Oracle PLSQL语法。
p.campbell 2011年

3
@ p.campbell,这不是我的问题……
Muhd

1
哦,我明白了。因此,您复制粘贴了问题正文,但将其修改为包括Oracle位。
p.campbell 2011年

2
是的 这可能不是最好的示例,因为“ desc”是保留字,但是哦。
Muhd

Answers:


512

这称为相关更新

UPDATE table1 t1
   SET (name, desc) = (SELECT t2.name, t2.desc
                         FROM table2 t2
                        WHERE t1.id = t2.id)
 WHERE EXISTS (
    SELECT 1
      FROM table2 t2
     WHERE t1.id = t2.id )

假设联接导致键保留视图,您还可以

UPDATE (SELECT t1.id, 
               t1.name name1,
               t1.desc desc1,
               t2.name name2,
               t2.desc desc2
          FROM table1 t1,
               table2 t2
         WHERE t1.id = t2.id)
   SET name1 = name2,
       desc1 = desc2

8
在您的第一个代码示例中:外部WHERE子句是否需要正确的结果?还是仅使用它来加快查询速度?
Mathias Bader

41
@totoro-在第一个示例中,如果中没有匹配的行,则WHERE EXISTS阻止您更新其中的行。如果没有它,将更新in中的每一行,如果in中没有匹配的行,则将值设置为。通常这不是您想要发生的事情,因此通常是需要的。t1t2t1NULLt2WHERE EXISTS
贾斯汀·凯夫

3
值得补充的是,SELECT ... FROM t2 必须导致唯一的行。这意味着您必须在包含唯一键的所有字段上进行选择-非唯一的主键是不够的。没有唯一性,您将被简化为@PaulKarr的循环 -如果没有唯一的相关性,则可能为每个源行更新一个以上的目标行。
Andrew Leach 2013年

2
对可更新密钥保存要求说明加入:asktom.oracle.com/pls/asktom/...
Vadzim

1
@RachitSharma-这意味着您的子查询(来自的查询table2)针对一个或多个table1值返回多行,而Oracle不知道您要使用哪一个。通常,这意味着您需要优化子查询,以便它返回单个不同的行。
贾斯汀·凯夫

132

试试这个:

MERGE INTO table1 t1
USING
(
-- For more complicated queries you can use WITH clause here
SELECT * FROM table2
)t2
ON(t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET
t1.name = t2.name,
t1.desc = t2.desc;

4
确实非常快,1,159477行在15,5s中合并
jefissu

3
希望2015年以后访问此问题的每个人都注意到此答案。请注意,这也适用,如果table1table2是同一个表,只是照顾的ON双组分和WHERE-clause的SELECT的语句来table2
sjngm

1
我发现每次需要进行另一次合并时,我都会不断回到这个答案中寻求灵感。我可能会把它打印出来并装在我的墙上
arnehehe

像魅力一样工作!!谢谢!
davidwillianx

从表2的SELECT DISTINCT ID,FIELD1,FIELD1中,ID不为空
Joseph Poirier

17

尝试

UPDATE Table1 T1 SET
T1.name = (SELECT T2.name FROM Table2 T2 WHERE T2.id = T1.id),
T1.desc = (SELECT T2.desc FROM Table2 T2 WHERE T2.id = T1.id)
WHERE T1.id IN (SELECT T2.id FROM Table2 T2 WHERE T2.id = T1.id);

4
这样做的缺点是SELECT语句重复了3次。在复杂的示例中,这可能会破坏交易。
DavidBalažic17年

9
Update table set column = (select...)

从来没有为我工作,因为set只期望1值-SQL错误:ORA-01427:单行子查询返回多个行。

解决方法是:

BEGIN
For i in (select id, name, desc from table1) 
LOOP
Update table2 set name = i.name, desc = i.desc where id = i.id;
END LOOP;
END;

这就是您在SQLDeveloper工作表上准确运行它的方式。他们说这很慢,但这是在这种情况下对我有用的唯一解决方案。


有人可以解释一下为什么这在信誉上应得-2吗?大声笑。
波城(Pau Karr)2013年

13
我没有降低速度,但这不是一个好的解决方案。首先:如果子选择返回多个值,则for循环将对某些/所有记录多次重写table2上的名称(不干净)。其次:没有order by子句,因此这将以不可预测的方式发生(即,无序数据中的最后一个值获胜)。第三:会慢很多。假设要使用for循环的结果,则可以以某种受控方式重写原始子选择,以便仅为每个记录返回1个值...最简单的方法是(select min(name)...)
Alternator

这正是我所需要的。谢谢(+1)
罗伯·凯悦

3
如果在子查询中获得多个值,则可以重新考虑该查询,并将DISTINCT或GROUP BY与MIN,MAX结合使用。只是一个主意。
弗朗西斯

长话短说:如果您完全可以避免它,则永远不要在T-SQL语句中使用任何类型的LOOP。就个人而言,如果不是在0.001%的时间内没有其他解决方案,我什至认为它甚至不是T-SQL中的可用功能。T-SQL被设计为基于集合的,因此它可以处理整个数据集。它不应用于逐行处理数据。
Ray K.

8

对于'in'子句,这似乎是一个更好的答案,该子句允许用于联接的多个键

update fp_active set STATE='E', 
   LAST_DATE_MAJ = sysdate where (client,code) in (select (client,code) from fp_detail
  where valid = 1) ...

关键是要在“ in”之前的where子句中的括号中包含要用作键的列,并使select语句的括号中具有相同的列名。其中的(column1,column2)在(从“我想要的集合”表中选择(column1,column2)中;


链接已过期。(404
小飞象

-3

如果您的表t1及其备份t2有很多列,这是一种紧凑的方法。

另外,我的相关问题是仅修改了部分列,并且许多行都没有对这些列进行编辑,因此我想让这些列留空-基本上是从整个表的备份中还原部分列。如果只想还原所有行,请跳过where子句。

当然,更简单的方法是删除并插入为select,但是在我的情况下,我需要一个仅包含更新的解决方案。

诀窍是,当您从具有重复列名的一对表中选择*时,第二个表将被命名为_1。所以这是我想出的:

  update (
    select * from t1 join t2 on t2.id = t1.id
    where id in (
      select id from (
        select id, col1, col2, ... from t2
        minus select id, col1, col2, ... from t1
      )
    )
  ) set col1=col1_1, col2=col2_1, ...

这在Oracle 11g中对我不起作用。您可以创建此方法的有效示例吗?
乔恩·海勒

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.