在Oracle Update语句中使用子查询而不是表名

时间:2008-10-23 15:30:38

标签: sql oracle

我需要编写一个更新语句,该语句使用多个表来确定要更新的行,因为在Oracle中,不允许多个表。以下查询将返回“ORA-00971:缺少SET关键字”错误

UPDATE
  TABLE1 a,
  TABLE2 b
SET
  a.COL1 = 'VALUE'
WHERE
  a.FK = b.PK
  AND b.COL2 IN ('SET OF VALUES')

在oracle上查找UPDATE语句语法,我找到了以下link,它表明您可以使用子查询代替表名。

当我尝试编写这样的查询时,我得到了一个“ORA-01779:无法修改映射到非密钥保留表的列”

UPDATE
  (
    SELECT
      a.COL1
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
  ) update_tbl
SET
  update_tbl.COL1 = 'VALUE'

我确实使用EXISTS语句重写了查询(如下所示)并且它工作正常,但仍然想知道如何完成。

UPDATE
  TABLE1 update_tbl
SET
  update_tbl.COL1 = 'VALUE'
WHERE
  EXISTS (
    SELECT
      1
    FROM
      TABLE1 a
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
      AND update_tbl.PK = a.PK
  )

谢谢! -Nate

6 个答案:

答案 0 :(得分:7)

另一种选择:

UPDATE TABLE1 a
SET a.COL1 = 'VALUE'
WHERE a.FK IN
( SELECT b.PK FROM TABLE2 b
  WHERE b.COL2 IN ('SET OF VALUES')
)

如果(a)视图包含TABLE1的声明的 PK,那么您的第二个示例将起作用:

UPDATE
  (
    SELECT
      a.COL1, a.PKCOL
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
  ) update_tbl
SET
  update_tbl.COL1 = 'VALUE'

...和(b)TABLE1.FK是TABLE2的声明的外键

(声明我的意思是存在约束并启用)。

答案 1 :(得分:7)

我发现将SELECT语句转换为UPDATE的一种很好,快速,一致的方法是根据ROWID进行更新。

UPDATE
  TABLE1
SET
  COL1 = 'VALUE'
WHERE
  ROWID in
    (
    SELECT
      a.rowid
    FROM
      TABLE1 a,
      TABLE2 b
    WHERE
      a.FK = b.PK
      AND b.COL2 IN ('SET OF VALUES')
    )

因此,您的内部查询正在定义要更新的行。

答案 2 :(得分:3)

您的示例的语法很好,但Oracle要求子查询包含主键。这是一个非常重要的限制。

在相关说明中,您还可以使用括号在IN语句中使用2个或更多字段,如:

UPDATE
  TABLE1 update_tbl
SET
  update_tbl.COL1 = 'VALUE'
WHERE
  (update_tbl.PK1, update_tbl.pk2) in(
                      select some_field1, some_field2
                      from some_table st
                      where st.some_fields = 'some conditions'
                      );

答案 3 :(得分:2)

当您执行更新时,您显然只能告诉系统将值更新为单个新值 - 告诉它将“X”更新为“Y”和“Z”没有意义。因此,当您对内联视图的结果进行更新时,Oracle会检查是否存在足够的约束以防止修改后的列可能会更新两次。

在您的情况下,我希望TABLE2.PK实际上不是声明的主键。如果你对那个columnthen放置一个主要或唯一的约束,你就可以去。

有一个未记录的提示可以通过Oracle内部使用的更新连接基数检查,但我不建议使用它。

一种解决方法是使用MERGE语句,该语句不受同一测试的约束。

答案 4 :(得分:1)

我在这里找到了我需要的东西: Useful SQL Commands

我需要用连接的结果更新一个表 我尝试了上述解决方案但没有成功:(

以下是我在上面指出的页面的摘录 使用游标我能够成功完成任务 我确信还有另一个解决方案,但是这个解决方案是这样的...

DECLARE

 /* Output variables to hold the result of the query: */
 a T1.e%TYPE;
 b T2.f%TYPE;
 c T2.g%TYPE;

 /* Cursor declaration: */
 CURSOR T1Cursor IS
   SELECT T1.e, T2.f, T2.g
   FROM T1, T2
   WHERE T1.id = T2.id AND T1.e <> T2.f

 FOR UPDATE;

BEGIN

  OPEN T1Cursor;

  LOOP

    /* Retrieve each row of the result of the above query
    into PL/SQL variables: */
    FETCH T1Cursor INTO a, b;

    /* If there are no more rows to fetch, exit the loop: */
    EXIT WHEN T1Cursor%NOTFOUND;

    /* Delete the current tuple: */
    DELETE FROM T1 WHERE CURRENT OF T1Cursor;

    /* Insert the reverse tuple: */
    INSERT INTO T1 VALUES(b, a);

    /* Here is my stuff using the variables to update my table */
    UPDATE T2
    SET T2.f = a
    WHERE T2.id = c;

  END LOOP;

  /* Free cursor used by the query. */
  CLOSE T1Cursor;

END;
.
run;

注意:不要忘记提交;-)

答案 5 :(得分:0)

UPDATE子句中查询结果集中的每一行必须映射回您尝试更新的表中的一行且只有一行,并且Oracle可以自动跟踪。由于查询实际上是一个视图,因此考虑它的一种方法是Oracle需要能够将视图连接回目标表,以便知道要更新的行。

这实质上意味着您需要在该查询中包含目标表的主键。您也许可以使用其他一些独特的索引字段,但我不能保证Oracle DBMS足够聪明以允许它。