在Oracle上使用内部联接更新语句


297

我有一个在MySQL中运行良好的查询,但是在Oracle上运行它时出现以下错误:

SQL错误:ORA-00933:SQL命令未正确终止
00933。00000-“ SQL命令未正确终止”

查询是:

UPDATE table1
INNER JOIN table2 ON table1.value = table2.DESC
SET table1.value = table2.CODE
WHERE table1.UPDATETYPE='blah';

当我尝试在Oracle中设置table2来测试答案时,我发现Oracle拒绝DESC作为列名。
Janek Bogucki 2010年

抱歉,我只是将原始列名缩写为
dsc

Answers:


411

该语法在Oracle中无效。你可以这样做:

UPDATE table1 SET table1.value = (SELECT table2.CODE
                                  FROM table2 
                                  WHERE table1.value = table2.DESC)
WHERE table1.UPDATETYPE='blah'
AND EXISTS (SELECT table2.CODE
            FROM table2 
            WHERE table1.value = table2.DESC);

或者您可以执行以下操作:

UPDATE 
(SELECT table1.value as OLD, table2.CODE as NEW
 FROM table1
 INNER JOIN table2
 ON table1.value = table2.DESC
 WHERE table1.UPDATETYPE='blah'
) t
SET t.OLD = t.NEW

这取决于Oracle是否认为内联视图可更新(要更新第二条语句取决于此处列出的某些规则 )。


5
我做了第二个示例,但是必须在选择的列名称中添加别名,然后在SET中通过它们的名称引用它们,但是它起作用了,谢谢
Gustavo Rubio

41
第二个示例的好处是允许您在实际执行更新之前测试SQL。
Daniel Reis 2012年

10
第二个例子对我有用。我喜欢它,因为它看起来干净而且可读。不知道两者之间在性能方面的优缺点。但是,我现在并不担心,因为我将其用于一次性脚本来纠正不良数据。
nemo 2012年

5
第二为我工作:)。甲骨文是一只强壮但怪异的动物:/
elrado

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

202

用这个:

MERGE
INTO    table1 trg
USING   (
        SELECT  t1.rowid AS rid, t2.code
        FROM    table1 t1
        JOIN    table2 t2
        ON      table1.value = table2.DESC
        WHERE   table1.UPDATETYPE='blah'
        ) src
ON      (trg.rowid = src.rid)
WHEN MATCHED THEN UPDATE
    SET trg.value = code;

2
效果很好,但是Oracle要求我说merge into table 1 t诸如此类。
Michael-O

1
参加聚会晚了,但这仍然是一个很好的话题。我需要知道,“ ...我错过了什么吗?主表“ table1”。在使用中,table1的别名为t1。Table2,别名为t2,但在ON中,引用为...?外部表1-不是t1-这是对外部表的引用还是类型?表2?不是t2?Je suis感到困惑。更好别名的粉丝...
2014年

此处只是一点,如果您的密钥(trg.rowid或src.rid)具有一个重复项,则此子句会引发错误:ora-30926.ora-code.com
Henrique

@Marc在中ONtrg是主表table1(在逻辑上为“外部”表)的别名,并src引用该USING组(在逻辑上为“内部表”)。但是,是的,本来可以更好地引用它,但我能够遵循它。
vapcguy

1
@supernova:Tony的答案是更新内联视图。在某些情况下这可以工作,但是视图必须“保留键”(每个联接表必须在其主键或唯一字段集上相等联接)。这样可以确保目标表中的每条记录最多对结果行集中的一条记录有贡献,因此,目标表中的每条记录最多只能更新一次。
Quassnoi

25

MERGEwith WHERE子句:

MERGE into table1
USING table2
ON (table1.id = table2.id)
WHEN MATCHED THEN UPDATE SET table1.startdate = table2.start_date
WHERE table1.startdate > table2.start_date;

您需要该WHERE子句,因为该子句中引用的列ON无法更新。


这个版本可以说是更干净,但是它不是触发友好的,因为我知道没有办法避免使用此语法为未更改的行触发更新触发器。(我假设更改的行需要触发器。)
sf_jeff


11

不要使用上面的一些答案。

有些人建议使用嵌套的SELECT,但不要这样做,这太慢了。如果您有很多记录要更新,请使用join,例如:

update (select bonus 
        from employee_bonus b 
        inner join employees e on b.employee_id = e.employee_id 
        where e.bonus_eligible = 'N') t
set t.bonus = 0;

有关更多详细信息,请参见此链接。 http://geekswithblogs.net/WillSmith/archive/2008/06/18/oracle-update-with-join-again.aspx

另外,请确保要连接的所有表上都有主键。


7

如图所示这里,第一个解决方案由托尼·安德鲁斯提出的一般语法是:

update some_table s
set   (s.col1, s.col2) = (select x.col1, x.col2
                          from   other_table x
                          where  x.key_value = s.key_value
                         )
where exists             (select 1
                          from   other_table x
                          where  x.key_value = s.key_value
                         )

我认为这很有趣,尤其是如果您要更新多个字段。


这对我不起作用。它更新整个表。
Natassia Tavares

3

以下语法对我有用。

UPDATE
(SELECT A.utl_id,
    b.utl1_id
    FROM trb_pi_joint A
    JOIN trb_tpr B
    ON A.tp_id=B.tp_id Where A.pij_type=2 and a.utl_id is null
)
SET utl_id=utl1_id;

@JimGarrison请重新编辑此答案,以便删除我的下注...。我试图使用此语法,但未更新我的表。我发现了原因-我SET正在做a,REPLACE并且我试图清空该列中的特定字符串-原来Oracle将其''视为null,并且该字段不能为null。我以为语法只是更新一个临时表,而不是真正的临时表,但是我错了。
vapcguy '16

2

对于表2 使用描述而不是desc,

update
  table1
set
  value = (select code from table2 where description = table1.value)
where
  exists (select 1 from table2 where description = table1.value)
  and
  table1.updatetype = 'blah'
;

为什么您要在table2上触发两个单独的查询
Jitendra Vispute

2

oracle工作正常

merge into table1 t1
using (select * from table2) t2
on (t1.empid = t2.empid)
when matched then update set t1.salary = t2.salary

可以通过在其末尾添加逗号来设置多个属性。t1.First_Name = t2.FirstName, t1.Last_Name = t2.LastName在“ UserName”列(t1.UserName = t2.UserName)上对其进行匹配后,我需要对一个表进行处理,以从名为UserInfo(select * from UserInfo) t2)的表中检索其名称。数据库是如此,它在任何地方都使用UserName作为UserInfo的主键,而不是直接在表中放置FirstName和LastName。这样解决了!
vapcguy '16

这个答案对Quassnoi在您提出的五年之前已经提供的答案没有任何帮助。
牧草

0
UPDATE table1 t1
SET t1.value = 
    (select t2.CODE from table2 t2 
     where t1.value = t2.DESC) 
WHERE t1.UPDATETYPE='blah';

0
UPDATE IP_ADMISSION_REQUEST ip1
SET IP1.WRIST_BAND_PRINT_STATUS=0
WHERE IP1.IP_ADM_REQ_ID        =
  (SELECT IP.IP_ADM_REQ_ID
  FROM IP_ADMISSION_REQUEST ip
  INNER JOIN VISIT v
  ON ip.ip_visit_id=v.visit_id
  AND v.pat_id     =3702
  ); `enter code here`

0

出于完整性的考虑,并且因为我们在谈论Oracle,所以也可以这样做:

declare
begin
  for sel in (
    select table2.code, table2.desc
    from table1
    join table2 on table1.value = table2.desc
    where table1.updatetype = 'blah'
  ) loop
    update table1 
    set table1.value = sel.code
    where table1.updatetype = 'blah' and table1.value = sel.desc;    
  end loop;
end;
/

1
这可以做到,但这是最慢的方法。
APC

-1
UPDATE (SELECT T.FIELD A, S.FIELD B
FROM TABLE_T T INNER JOIN TABLE_S S
ON T.ID = S.ID)
SET B = A;

A和B是别名字段,您无需指向表。


1
嗨,丹 您正在发布一个已经很不错的答案的老问题。您能解释什么时候您的问题比其他解决方案更可取吗?
诺埃尔·威德默

1
当然,我已经看到了一个答案,其中b = a是通过指向表名(table1.B = table2.A)而写的,但是没有必要指向该表。
丹·安德森

您实际上是在更新视图中的字段,这些字段已映射到表。如果内部视图使用别名h,则“自我记录”版本将为“ set hb = ha”。
sf_jeff

-4
update table1  a 
   set a.col1='Y' 
 where exists(select 1 
                from table2 b
               where a.col1=b.col1 
                 and a.col2=b.col2
             )
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.